PageRenderTime 53ms CodeModel.GetById 23ms RepoModel.GetById 0ms app.codeStats 0ms

/Raven.Database/Server/Controllers/DebugController.cs

https://github.com/nwendel/ravendb
C# | 481 lines | 418 code | 63 blank | 0 comment | 34 complexity | 10e75618ac362cab4f46c503a8d2d959 MD5 | raw file
Possible License(s): MPL-2.0-no-copyleft-exception, BSD-3-Clause, CC-BY-SA-3.0
  1. using System;
  2. using System.Collections;
  3. using System.Collections.Generic;
  4. using System.ComponentModel;
  5. using System.Diagnostics;
  6. using System.IO;
  7. using System.IO.Compression;
  8. using System.Linq;
  9. using System.Net;
  10. using System.Net.Http;
  11. using System.Net.Http.Headers;
  12. using System.Security.Principal;
  13. using System.Threading.Tasks;
  14. using System.Web.Http;
  15. using System.Web.Http.Controllers;
  16. using System.Web.Http.Routing;
  17. using Raven.Abstractions;
  18. using Raven.Abstractions.Data;
  19. using Raven.Abstractions.Logging;
  20. using Raven.Database.Bundles.SqlReplication;
  21. using Raven.Database.Extensions;
  22. using Raven.Database.Indexing;
  23. using Raven.Database.Linq;
  24. using Raven.Database.Linq.Ast;
  25. using Raven.Database.Server.Abstractions;
  26. using Raven.Database.Storage;
  27. using Raven.Database.Tasks;
  28. using Raven.Database.Util;
  29. using Raven.Imports.Newtonsoft.Json;
  30. using Raven.Json.Linq;
  31. using Index = Raven.Database.Indexing.Index;
  32. using IOExtensions = Raven.Database.Extensions.IOExtensions;
  33. namespace Raven.Database.Server.Controllers
  34. {
  35. [RoutePrefix("")]
  36. public class DebugController : RavenDbApiController
  37. {
  38. [HttpGet]
  39. [Route("debug/prefetch-status")]
  40. [Route("databases/{databaseName}/debug/prefetch-status")]
  41. public HttpResponseMessage PrefetchingQueueStatus()
  42. {
  43. return GetMessageWithObject(DebugInfoProvider.GetPrefetchingQueueStatusForDebug(Database));
  44. }
  45. [HttpPost]
  46. [Route("debug/format-index")]
  47. [Route("databases/{databaseName}/debug/format-index")]
  48. public async Task<HttpResponseMessage> FormatIndex()
  49. {
  50. var array = await ReadJsonArrayAsync();
  51. var results = new string[array.Length];
  52. for (int i = 0; i < array.Length; i++)
  53. {
  54. var value = array[i].Value<string>();
  55. try
  56. {
  57. results[i] = IndexPrettyPrinter.Format(value);
  58. }
  59. catch (Exception e)
  60. {
  61. results[i] = "Could not format:" + Environment.NewLine +
  62. value + Environment.NewLine + e;
  63. }
  64. }
  65. return GetMessageWithObject(results);
  66. }
  67. [HttpGet]
  68. [Route("debug/plugins")]
  69. [Route("databases/{databaseName}/debug/plugins")]
  70. public HttpResponseMessage Plugins()
  71. {
  72. return GetMessageWithObject(Database.PluginsInfo);
  73. }
  74. [HttpGet]
  75. [Route("debug/changes")]
  76. [Route("databases/{databaseName}/debug/changes")]
  77. public HttpResponseMessage Changes()
  78. {
  79. return GetMessageWithObject(Database.TransportState.DebugStatuses);
  80. }
  81. [HttpGet]
  82. [Route("debug/sql-replication-stats")]
  83. [Route("databases/{databaseName}/debug/sql-replication-stats")]
  84. public HttpResponseMessage SqlReplicationStats()
  85. {
  86. var task = Database.StartupTasks.OfType<SqlReplicationTask>().FirstOrDefault();
  87. if (task == null)
  88. return GetMessageWithObject(new
  89. {
  90. Error = "SQL Replication bundle is not installed"
  91. }, HttpStatusCode.NotFound);
  92. return GetMessageWithObject(task.Statistics);
  93. }
  94. [HttpGet]
  95. [Route("debug/metrics")]
  96. [Route("databases/{databaseName}/debug/metrics")]
  97. public HttpResponseMessage Metrics()
  98. {
  99. return GetMessageWithObject(Database.CreateMetrics());
  100. }
  101. [HttpGet]
  102. [Route("debug/config")]
  103. [Route("databases/{databaseName}/debug/config")]
  104. public HttpResponseMessage Config()
  105. {
  106. return GetMessageWithObject(DebugInfoProvider.GetConfigForDebug(Database));
  107. }
  108. [HttpGet]
  109. [Route("debug/docrefs")]
  110. [Route("databases/{databaseName}/debug/docrefs")]
  111. public HttpResponseMessage Docrefs(string id)
  112. {
  113. var op = GetQueryStringValue("op") == "from" ? "from" : "to";
  114. var totalCountReferencing = -1;
  115. List<string> results = null;
  116. Database.TransactionalStorage.Batch(accessor =>
  117. {
  118. totalCountReferencing = accessor.Indexing.GetCountOfDocumentsReferencing(id);
  119. var documentsReferencing =
  120. op == "from"
  121. ? accessor.Indexing.GetDocumentsReferencesFrom(id)
  122. : accessor.Indexing.GetDocumentsReferencing(id);
  123. results = documentsReferencing.Skip(GetStart()).Take(GetPageSize(Database.Configuration.MaxPageSize)).ToList();
  124. });
  125. return GetMessageWithObject(new
  126. {
  127. TotalCountReferencing = totalCountReferencing,
  128. Results = results
  129. });
  130. }
  131. [HttpPost]
  132. [Route("debug/index-fields")]
  133. [Route("databases/{databaseName}/debug/index-fields")]
  134. public async Task<HttpResponseMessage> IndexFields()
  135. {
  136. var indexStr = await ReadStringAsync();
  137. var mapDefinition = indexStr.Trim().StartsWith("from")
  138. ? QueryParsingUtils.GetVariableDeclarationForLinqQuery(indexStr, true)
  139. : QueryParsingUtils.GetVariableDeclarationForLinqMethods(indexStr, true);
  140. var captureSelectNewFieldNamesVisitor = new CaptureSelectNewFieldNamesVisitor();
  141. mapDefinition.AcceptVisitor(captureSelectNewFieldNamesVisitor, null);
  142. return GetMessageWithObject(new { captureSelectNewFieldNamesVisitor.FieldNames });
  143. }
  144. [HttpGet]
  145. [Route("debug/list")]
  146. [Route("databases/{databaseName}/debug/list")]
  147. public HttpResponseMessage List(string id)
  148. {
  149. var listName = id;
  150. var key = InnerRequest.RequestUri.ParseQueryString()["key"];
  151. if (key == null)
  152. throw new ArgumentException("Key query string variable is mandatory");
  153. ListItem listItem = null;
  154. Database.TransactionalStorage.Batch(accessor =>
  155. {
  156. listItem = accessor.Lists.Read(listName, key);
  157. });
  158. if (listItem == null)
  159. return GetEmptyMessage(HttpStatusCode.NotFound);
  160. return GetMessageWithObject(listItem);
  161. }
  162. [HttpGet]
  163. [Route("debug/list-all")]
  164. [Route("databases/{databaseName}/debug/list-all")]
  165. public HttpResponseMessage ListAll(string id)
  166. {
  167. var listName = id;
  168. List<ListItem> listItems = null;
  169. Database.TransactionalStorage.Batch(accessor =>
  170. {
  171. listItems = accessor.Lists.Read(listName, Etag.Empty, null, GetPageSize(Database.Configuration.MaxPageSize)).ToList();
  172. });
  173. if (listItems == null)
  174. return GetEmptyMessage(HttpStatusCode.NotFound);
  175. return GetMessageWithObject(listItems);
  176. }
  177. [HttpGet]
  178. [Route("debug/queries")]
  179. [Route("databases/{databaseName}/debug/queries")]
  180. public HttpResponseMessage Queries()
  181. {
  182. return GetMessageWithObject(Database.WorkContext.CurrentlyRunningQueries);
  183. }
  184. [HttpGet]
  185. [Route("debug/suggest-index-merge")]
  186. [Route("databases/{databaseName}/debug/suggest-index-merge")]
  187. public HttpResponseMessage IndexMerge()
  188. {
  189. var mergeIndexSuggestions = Database.WorkContext.IndexDefinitionStorage.ProposeIndexMergeSuggestions();
  190. return GetMessageWithObject(mergeIndexSuggestions);
  191. }
  192. [HttpGet]
  193. [Route("debug/sl0w-d0c-c0unts")]
  194. [Route("databases/{databaseName}/debug/sl0w-d0c-c0unts")]
  195. public HttpResponseMessage SlowDocCounts()
  196. {
  197. DebugDocumentStats stat = null;
  198. Database.TransactionalStorage.Batch(accessor =>
  199. {
  200. stat = accessor.Documents.GetDocumentStatsVerySlowly();
  201. });
  202. return GetMessageWithObject(stat);
  203. }
  204. [HttpGet]
  205. [HttpPost]
  206. [Route("debug/user-info")]
  207. [Route("databases/{databaseName}/debug/user-info")]
  208. public HttpResponseMessage UserInfo()
  209. {
  210. var principal = User;
  211. if (principal == null || principal.Identity == null || principal.Identity.IsAuthenticated == false)
  212. {
  213. var anonymous = new UserInfo
  214. {
  215. Remark = "Using anonymous user",
  216. IsAdminGlobal = DatabasesLandlord.SystemConfiguration.AnonymousUserAccessMode == AnonymousUserAccessMode.Admin
  217. };
  218. return GetMessageWithObject(anonymous);
  219. }
  220. var windowsPrincipal = principal as WindowsPrincipal;
  221. if (windowsPrincipal != null)
  222. {
  223. var windowsUser = new UserInfo
  224. {
  225. Remark = "Using windows auth",
  226. User = windowsPrincipal.Identity.Name,
  227. IsAdminGlobal =
  228. windowsPrincipal.IsAdministrator(DatabasesLandlord.SystemConfiguration.AnonymousUserAccessMode)
  229. };
  230. return GetMessageWithObject(windowsUser);
  231. }
  232. var principalWithDatabaseAccess = principal as PrincipalWithDatabaseAccess;
  233. if (principalWithDatabaseAccess != null)
  234. {
  235. var windowsUserWithDatabase = new UserInfo
  236. {
  237. Remark = "Using windows auth",
  238. User = principalWithDatabaseAccess.Identity.Name,
  239. IsAdminGlobal =
  240. principalWithDatabaseAccess.IsAdministrator(
  241. DatabasesLandlord.SystemConfiguration.AnonymousUserAccessMode),
  242. IsAdminCurrentDb = principalWithDatabaseAccess.IsAdministrator(Database),
  243. Databases =
  244. principalWithDatabaseAccess.AdminDatabases.Concat(
  245. principalWithDatabaseAccess.ReadOnlyDatabases)
  246. .Concat(principalWithDatabaseAccess.ReadWriteDatabases)
  247. .Select(db => new DatabaseInfo
  248. {
  249. Database = db,
  250. IsAdmin = principal.IsAdministrator(db)
  251. }).ToList(),
  252. AdminDatabases = principalWithDatabaseAccess.AdminDatabases,
  253. ReadOnlyDatabases = principalWithDatabaseAccess.ReadOnlyDatabases,
  254. ReadWriteDatabases = principalWithDatabaseAccess.ReadWriteDatabases
  255. };
  256. return GetMessageWithObject(windowsUserWithDatabase);
  257. }
  258. var oAuthPrincipal = principal as OAuthPrincipal;
  259. if (oAuthPrincipal != null)
  260. {
  261. var oAuth = new UserInfo
  262. {
  263. Remark = "Using OAuth",
  264. User = oAuthPrincipal.Name,
  265. IsAdminGlobal = oAuthPrincipal.IsAdministrator(DatabasesLandlord.SystemConfiguration.AnonymousUserAccessMode),
  266. IsAdminCurrentDb = oAuthPrincipal.IsAdministrator(Database),
  267. Databases = oAuthPrincipal.TokenBody.AuthorizedDatabases
  268. .Select(db => new DatabaseInfo
  269. {
  270. Database = db.TenantId,
  271. IsAdmin = principal.IsAdministrator(db.TenantId)
  272. }).ToList(),
  273. AccessTokenBody = oAuthPrincipal.TokenBody,
  274. };
  275. return GetMessageWithObject(oAuth);
  276. }
  277. var unknown = new UserInfo
  278. {
  279. Remark = "Unknown auth",
  280. Principal = principal
  281. };
  282. return GetMessageWithObject(unknown);
  283. }
  284. [HttpGet]
  285. [Route("debug/tasks")]
  286. [Route("databases/{databaseName}/debug/tasks")]
  287. public HttpResponseMessage Tasks()
  288. {
  289. return GetMessageWithObject(DebugInfoProvider.GetTasksForDebug(Database));
  290. }
  291. [HttpGet]
  292. [Route("debug/routes")]
  293. [Description(@"Output the debug information for all the supported routes in Raven Server.")]
  294. public HttpResponseMessage Routes()
  295. {
  296. var routes = new SortedDictionary<string, RouteInfo>();
  297. foreach (var route in ControllerContext.Configuration.Routes)
  298. {
  299. var inner = route as IEnumerable<IHttpRoute>;
  300. if (inner == null) continue;
  301. foreach (var httpRoute in inner)
  302. {
  303. var key = httpRoute.RouteTemplate;
  304. bool forDatabase = false;
  305. if (key.StartsWith("databases/{databaseName}/"))
  306. {
  307. key = key.Substring("databases/{databaseName}/".Length);
  308. forDatabase = true;
  309. }
  310. var data = new RouteInfo(key);
  311. if (routes.ContainsKey(key))
  312. data = routes[key];
  313. if (forDatabase)
  314. data.CanRunForSpecificDatabase = true;
  315. var actions = ((IEnumerable)httpRoute.DataTokens["actions"]).OfType<ReflectedHttpActionDescriptor>();
  316. foreach (var reflectedHttpActionDescriptor in actions)
  317. {
  318. foreach (var httpMethod in reflectedHttpActionDescriptor.SupportedHttpMethods)
  319. {
  320. if (data.Methods.Any(method => method.Name == httpMethod.Method))
  321. continue;
  322. string description = null;
  323. var descriptionAttibute =
  324. reflectedHttpActionDescriptor.MethodInfo.CustomAttributes.FirstOrDefault(attributeData => attributeData.AttributeType == typeof(DescriptionAttribute));
  325. if(descriptionAttibute != null)
  326. description = descriptionAttibute.ConstructorArguments[0].Value.ToString();
  327. data.Methods.Add(new Method
  328. {
  329. Name = httpMethod.Method,
  330. Description = description
  331. });
  332. }
  333. }
  334. routes[key] = data;
  335. }
  336. }
  337. return GetMessageWithObject(routes);
  338. }
  339. [HttpGet]
  340. [Route("debug/currently-indexing")]
  341. [Route("databases/{databaseName}/debug/currently-indexing")]
  342. public HttpResponseMessage CurrentlyIndexing()
  343. {
  344. return GetMessageWithObject(DebugInfoProvider.GetCurrentlyIndexingForDebug(Database));
  345. }
  346. [HttpGet]
  347. [Route("debug/request-tracing")]
  348. [Route("databases/{databaseName}/debug/request-tracing")]
  349. public HttpResponseMessage RequestTracing()
  350. {
  351. return GetMessageWithObject(DebugInfoProvider.GetRequestTrackingForDebug(RequestManager, DatabaseName));
  352. }
  353. [HttpGet]
  354. [Route("debug/identities")]
  355. [Route("databases/{databaseName}/debug/identities")]
  356. public HttpResponseMessage Identities()
  357. {
  358. var start = GetStart();
  359. var pageSize = GetPageSize(1024);
  360. long totalCount = 0;
  361. IEnumerable<KeyValuePair<string, long>> identities = null;
  362. Database.TransactionalStorage.Batch(accessor => identities = accessor.General.GetIdentities(start, pageSize, out totalCount));
  363. return GetMessageWithObject(new
  364. {
  365. TotalCount = totalCount,
  366. Identities = identities
  367. });
  368. }
  369. [HttpGet]
  370. [Route("databases/{databaseName}/debug/info-package")]
  371. [Route("debug/info-package")]
  372. public HttpResponseMessage InfoPackage()
  373. {
  374. var tempFileName = Path.Combine(Path.GetTempPath(), Path.GetRandomFileName());
  375. try
  376. {
  377. using (var file = new FileStream(tempFileName, FileMode.Create))
  378. using (var package = new ZipArchive(file, ZipArchiveMode.Create))
  379. {
  380. DebugInfoProvider.CreateInfoPackageForDatabase(package, Database, RequestManager);
  381. }
  382. var response = new HttpResponseMessage();
  383. response.Content = new StreamContent(new FileStream(tempFileName, FileMode.Open, FileAccess.Read))
  384. {
  385. Headers =
  386. {
  387. ContentDisposition = new ContentDispositionHeaderValue("attachment")
  388. {
  389. FileName = string.Format("Debug-Info-{0}.zip", SystemTime.UtcNow),
  390. },
  391. ContentType = new MediaTypeHeaderValue("application/octet-stream")
  392. }
  393. };
  394. return response;
  395. }
  396. finally
  397. {
  398. IOExtensions.DeleteFile(tempFileName);
  399. }
  400. }
  401. }
  402. public class RouteInfo
  403. {
  404. public string Key { get; set; }
  405. public List<Method> Methods { get; set; }
  406. public bool CanRunForSpecificDatabase { get; set; }
  407. public RouteInfo(string key)
  408. {
  409. Key = key;
  410. Methods = new List<Method>();
  411. }
  412. }
  413. public class Method
  414. {
  415. public string Name { get; set; }
  416. public string Description { get; set; }
  417. }
  418. }