251 строка
7.0 KiB
Plaintext
251 строка
7.0 KiB
Plaintext
{
|
|
"cells": [
|
|
{
|
|
"cell_type": "markdown",
|
|
"metadata": {},
|
|
"source": [
|
|
"# The Plan\n",
|
|
"Everything we know about the plan.\n",
|
|
"\n",
|
|
"### Instructions\n",
|
|
"1. Run all cells! (click on Menu > Cell > Run All Cells)\n",
|
|
"1. View report at the bottom."
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": null,
|
|
"metadata": {
|
|
"inputHidden": false,
|
|
"outputHidden": false,
|
|
"tags": [
|
|
"parameters"
|
|
]
|
|
},
|
|
"outputs": [],
|
|
"source": [
|
|
"#planId = \"98db70e2-cee5-4e2d-ae15-dca389fa8f41\"\n",
|
|
"planId = \"f38f1a4b-49d7-4f08-a9b9-c81b2c39aff6\""
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": null,
|
|
"metadata": {
|
|
"inputHidden": false,
|
|
"outputHidden": false
|
|
},
|
|
"outputs": [],
|
|
"source": [
|
|
"%%capture \n",
|
|
"# install packages, setup workspace root\n",
|
|
"!pip install --upgrade azure-kusto-notebooks plotly\n",
|
|
"import os\n",
|
|
"from azure.kusto.notebooks import utils as akn\n",
|
|
"import pandas as pd\n",
|
|
"pd.options.display.html.table_schema = True\n",
|
|
"\n",
|
|
"# cwd should be workspace root\n",
|
|
"if os.path.basename(os.getcwd()) == 'devops-pipelines':\n",
|
|
" os.chdir(os.pardir)"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": null,
|
|
"metadata": {
|
|
"inputHidden": false,
|
|
"outputHidden": false
|
|
},
|
|
"outputs": [],
|
|
"source": [
|
|
"# authenticate kusto client\n",
|
|
"# you will need to copy the token into a browser window for AAD auth. \n",
|
|
"client = akn.get_client('https://vso.kusto.windows.net')"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": null,
|
|
"metadata": {
|
|
"inputHidden": false,
|
|
"outputHidden": false
|
|
},
|
|
"outputs": [],
|
|
"source": [
|
|
"# collect basic plan info\n",
|
|
"plan_info = akn.Query(\n",
|
|
" client, 'VSO', \n",
|
|
" path=os.path.join('devops-pipelines', 'queries', 'run', 'PlanInfo.csl'), \n",
|
|
" params={'OrchestrationId': akn.quote(planId)})\n",
|
|
"\n",
|
|
"# collect full plan history\n",
|
|
"what_happened = akn.Query(client, 'VSO',\n",
|
|
" path=os.path.join('devops-pipelines', 'queries', 'run', 'WhatHappened.csl'),\n",
|
|
" params={'OrchestrationId': akn.quote(planId)})\n",
|
|
"\n",
|
|
"# fetch data in parallel\n",
|
|
"akn.run((plan_info, what_happened))"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": null,
|
|
"metadata": {
|
|
"inputHidden": false,
|
|
"outputHidden": false
|
|
},
|
|
"outputs": [],
|
|
"source": [
|
|
"# draw basic info\n",
|
|
"\n",
|
|
"# compute relative time stamps\n",
|
|
"history = what_happened.dataframe\n",
|
|
"t0 = history['PreciseTimeStamp'].iloc[0]\n",
|
|
"history['Time'] = history.apply(lambda row: row['PreciseTimeStamp'] - t0, axis=1)\n",
|
|
"history.OrchestrationId = history.apply(lambda row: row.OrchestrationId[37:], axis=1)\n",
|
|
"\n",
|
|
"# record critical times\n",
|
|
"def find_time(message):\n",
|
|
" r = history[history.Message.str.startswith(message)]\n",
|
|
" if len(r.index) > 0:\n",
|
|
" return r['PreciseTimeStamp'].iloc[0]\n",
|
|
"\n",
|
|
"create_time = find_time('Created plan')\n",
|
|
"start_time = find_time('Started plan')\n",
|
|
"end_time = find_time('Completed orchestration with result')\n",
|
|
"total_duration = end_time - start_time if end_time and start_time else None\n",
|
|
"\n",
|
|
"import importlib\n",
|
|
"importlib.reload(akn)\n",
|
|
"# info will only exist if the plan has started at least one job :(\n",
|
|
"d = akn.pandas_row_to_dictionary(plan_info.dataframe)\n",
|
|
"d['create time'] = create_time\n",
|
|
"d['start time'] = start_time\n",
|
|
"d['end time'] = end_time\n",
|
|
"d['total duration'] = total_duration\n",
|
|
"r = akn.Report()\n",
|
|
"r.write(akn.to_md_table(d))\n",
|
|
"\n",
|
|
"from IPython.display import Markdown\n",
|
|
"Markdown(r.content)"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": null,
|
|
"metadata": {
|
|
"inputHidden": false,
|
|
"outputHidden": false
|
|
},
|
|
"outputs": [],
|
|
"source": [
|
|
"# SLA analysis\n",
|
|
"su = akn.quote(d.get('ScaleUnit', ''))\n",
|
|
"oids = [akn.quote(joid) for joid in d.get('JobOrchestrationIds', [])]\n",
|
|
"slas = [akn.Query(client, 'VSO', \n",
|
|
" os.path.join('devops-pipelines', 'queries', 'sla', 'SLAVisualization.csl'),\n",
|
|
" params=dict(ScaleUnit=su, OrchestrationId=oid)) \n",
|
|
" for oid in oids]\n",
|
|
"akn.run(slas)"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": null,
|
|
"metadata": {
|
|
"inputHidden": false,
|
|
"outputHidden": false
|
|
},
|
|
"outputs": [],
|
|
"source": [
|
|
"# draw all slas\n",
|
|
"from _plotly_future_ import v4_subplots\n",
|
|
"from plotly.subplots import make_subplots\n",
|
|
"import plotly.graph_objects as go\n",
|
|
"import math\n",
|
|
"if not slas:\n",
|
|
" print(\"There are no jobs associated with this plan.\")\n",
|
|
"else:\n",
|
|
" number_of_graphs = min(25, len(slas))\n",
|
|
" names = [n[37:] for n in d.get('JobOrchestrationIds',[])]\n",
|
|
" fig = make_subplots(cols=2, rows=int(math.ceil(number_of_graphs / 2)), \n",
|
|
" subplot_titles=names,\n",
|
|
" shared_xaxes=True, \n",
|
|
" vertical_spacing=0.1)\n",
|
|
"\n",
|
|
" for i in range(len(slas)):\n",
|
|
" df = slas[i].dataframe\n",
|
|
" row = int(i / 2) + 1\n",
|
|
" col = int(i % 2) + 1\n",
|
|
" name = names[i]\n",
|
|
" \n",
|
|
" df = slas[0].dataframe\n",
|
|
" fig.add_trace(go.Bar(x=df.PhaseName, y=df.PercentDifference, name=name), \n",
|
|
" row=row, col=col)\n",
|
|
" fig.update_xaxes(showgrid=False, tickangle=-60, automargin=True)\n",
|
|
" fig.update_xaxes(showgrid=True, zeroline=True, automargin=True)\n",
|
|
" fig.update_layout(height=150 * number_of_graphs, \n",
|
|
" width=1000, showlegend=False,\n",
|
|
" title_text=\"Analysis!\")\n",
|
|
"\n",
|
|
" fig.show()"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": null,
|
|
"metadata": {
|
|
"inputHidden": false,
|
|
"outputHidden": false
|
|
},
|
|
"outputs": [],
|
|
"source": [
|
|
"# draw full history\n",
|
|
"columns_to_ignore = ('source_', 'PreciseTimeStamp')\n",
|
|
"columns = ['Time'] + [c for c in history.columns if c not in columns_to_ignore and c != 'Time']\n",
|
|
"\n",
|
|
"from IPython.display import HTML\n",
|
|
"HTML(history[columns].to_html(index=False))"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": null,
|
|
"metadata": {
|
|
"inputHidden": false,
|
|
"outputHidden": false
|
|
},
|
|
"outputs": [],
|
|
"source": []
|
|
}
|
|
],
|
|
"metadata": {
|
|
"kernel_info": {
|
|
"name": "python3"
|
|
},
|
|
"kernelspec": {
|
|
"display_name": "Python 3",
|
|
"language": "python",
|
|
"name": "python3"
|
|
},
|
|
"language_info": {
|
|
"codemirror_mode": {
|
|
"name": "ipython",
|
|
"version": 3
|
|
},
|
|
"file_extension": ".py",
|
|
"mimetype": "text/x-python",
|
|
"name": "python",
|
|
"nbconvert_exporter": "python",
|
|
"pygments_lexer": "ipython3",
|
|
"version": "3.7.4"
|
|
},
|
|
"nteract": {
|
|
"version": "0.15.0"
|
|
}
|
|
},
|
|
"nbformat": 4,
|
|
"nbformat_minor": 2
|
|
}
|