hcs-cli 0.1.322__py3-none-any.whl → 0.1.323__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
hcs_cli/__init__.py CHANGED
@@ -1,3 +1,3 @@
1
- __version__ = "0.1.322"
1
+ __version__ = "0.1.323"
2
2
 
3
3
  from . import service as service # noqa: F401
@@ -28,16 +28,16 @@ def template(id: str, org: str, **kwargs):
28
28
  """Calendar plan for template."""
29
29
 
30
30
  org_id = cli.get_org_id(org)
31
- id = recent.require("template", id)
31
+ template_id = recent.require("template", id)
32
32
 
33
33
  # get plan for the template
34
34
 
35
- plan_id = f"CapacityOptimization-{id}"
35
+ plan_id = f"CapacityOptimization-{template_id}"
36
36
  plan = hcs.scm.plan.get(id=plan_id, org_id=org_id)
37
37
  if not plan:
38
38
  return None, 1
39
39
 
40
- template_data = hcs.template.get(org_id=org_id, id=id)
40
+ template_data = hcs.template.get(org_id=org_id, id=template_id)
41
41
 
42
42
  plan["meta"]["maxCapacity"] = template_data["sparePolicy"]["limit"]
43
43
  calendar = plan["calendar"]
@@ -46,6 +46,6 @@ def template(id: str, org: str, **kwargs):
46
46
  calendar[key]["idealCapacity"] = calendar[key]["forecastCapacity"]
47
47
 
48
48
  # fetch usage data for the usage chart
49
- usage = hcs.scm.template_usage(org_id, id)
49
+ usage = hcs.scm.template_usage(org_id, template_id)
50
50
 
51
- edit_plan(plan, template_data["name"], usage)
51
+ edit_plan(plan, template_data["name"], usage, template_id, org_id)
@@ -303,13 +303,13 @@
303
303
  <button class="tab-button active" data-tab="usage">Usage</button>
304
304
  <button class="tab-button" data-tab="capacity">Capacity</button>
305
305
  <button class="tab-button" data-tab="configuration">Configuration</button>
306
- <button class="tab-button" data-tab="data">Data</button>
307
306
  </div>
308
307
 
309
308
  <div class="tab-content active" id="usage-tab">
310
309
  <div style="margin-bottom: 10px; display: flex; justify-content: flex-end; align-items: center; gap: 10px;">
311
310
  <button id="reset-chart-zoom" style="padding: 6px 12px; background-color: #f0f0f0; border: 1px solid #ccc; border-radius: 4px; cursor: pointer; font-size: 12px; display: none;">Reset Time Range</button>
312
311
  <span id="selection-info" style="font-size: 12px; color: #666; font-weight: 500;">All timepoints</span>
312
+ <button class="copy-button" id="copy-data">Copy Data</button>
313
313
  </div>
314
314
  <div style="width: 100%; height: 400px; position: relative;">
315
315
  <div id="usage-chart" style="width: 100%; height: 100%; cursor: crosshair;"></div>
@@ -328,11 +328,6 @@
328
328
  <button class="copy-button" id="copy-config">Copy to Clipboard</button>
329
329
  <div class="code-container" id="config-json"></div>
330
330
  </div>
331
-
332
- <div class="tab-content" id="data-tab">
333
- <button class="copy-button" id="copy-data">Copy to Clipboard</button>
334
- <div class="code-container" id="chart-data-json"></div>
335
- </div>
336
331
  </div>
337
332
  </div>
338
333
 
@@ -845,6 +840,7 @@
845
840
  spare_capacity: usageChartData.spare_capacity,
846
841
  no_spare_error: usageChartData.no_spare_error,
847
842
  powered_on_vms: usageChartData.powered_on_vms,
843
+ powered_on_vms_predicated: usageChartData.powered_on_vms_predicated,
848
844
  consumed_capacity_predicated: usageChartData.consumed_capacity_predicated,
849
845
  spare_capacity_predicated: usageChartData.spare_capacity_predicated,
850
846
  no_spare_error_predicated: usageChartData.no_spare_error_predicated,
@@ -861,6 +857,7 @@
861
857
  chartData.spare_capacity = chartData.spare_capacity.slice(startIdx, endIdx + 1);
862
858
  chartData.no_spare_error = chartData.no_spare_error.slice(startIdx, endIdx + 1);
863
859
  chartData.powered_on_vms = chartData.powered_on_vms.slice(startIdx, endIdx + 1);
860
+ chartData.powered_on_vms_predicated = chartData.powered_on_vms_predicated.slice(startIdx, endIdx + 1);
864
861
  chartData.consumed_capacity_predicated = chartData.consumed_capacity_predicated.slice(startIdx, endIdx + 1);
865
862
  chartData.spare_capacity_predicated = chartData.spare_capacity_predicated.slice(startIdx, endIdx + 1);
866
863
  chartData.no_spare_error_predicated = chartData.no_spare_error_predicated.slice(startIdx, endIdx + 1);
@@ -892,7 +889,7 @@
892
889
  const option = {
893
890
  animation: false,
894
891
  title: {
895
- text: "Template Usage"
892
+ text: "Capacity Details: {{TEMPLATE_NAME}} ({{TEMPLATE_ID_PATH}})"
896
893
  },
897
894
  tooltip: {
898
895
  trigger: 'axis',
@@ -925,6 +922,20 @@
925
922
  splitIndex = usageChartData.x_axis.length;
926
923
  }
927
924
 
925
+ // Adjust splitIndex if we're viewing a filtered time range
926
+ if (currentFilteredStartIdx !== null && currentFilteredEndIdx !== null) {
927
+ const startIdx = Math.min(currentFilteredStartIdx, currentFilteredEndIdx);
928
+ const endIdx = Math.max(currentFilteredStartIdx, currentFilteredEndIdx);
929
+
930
+ // Adjust the split point relative to the filtered data
931
+ splitIndex = Math.max(0, splitIndex - startIdx);
932
+
933
+ // If the split point is beyond the filtered range, all data is predicated
934
+ if (splitIndex > (endIdx - startIdx + 1)) {
935
+ splitIndex = endIdx - startIdx + 1;
936
+ }
937
+ }
938
+
928
939
  const isInPredicatedArea = dataIndex >= splitIndex;
929
940
 
930
941
  // Filter series based on which area we're in
@@ -962,6 +973,7 @@
962
973
  'Spare Capacity',
963
974
  'Consumed Capacity',
964
975
  "No-spare Error",
976
+ 'Powered On VMs - Predicated',
965
977
  'Spare Capacity - Predicated',
966
978
  'Consumed Capacity - Predicated',
967
979
  "No-spare Error - Predicated",
@@ -973,6 +985,7 @@
973
985
  'Spare Capacity': true,
974
986
  'No-spare Error': true,
975
987
  'Optimized Capacity': true,
988
+ 'Powered On VMs - Predicated': true,
976
989
  'Spare Capacity - Predicated': false,
977
990
  'Consumed Capacity - Predicated': false,
978
991
  'No-spare Error - Predicated': true
@@ -1086,6 +1099,18 @@
1086
1099
  color: '#d66',
1087
1100
  data: chartData.no_spare_error_predicated,
1088
1101
  },
1102
+ {
1103
+ name: 'Powered On VMs - Predicated',
1104
+ type: 'line',
1105
+ lineStyle: { width: 1 },
1106
+ symbolSize: 0,
1107
+ stack: false,
1108
+ emphasis: {
1109
+ focus: 'none'
1110
+ },
1111
+ color: '#ff2222',
1112
+ data: chartData.powered_on_vms_predicated
1113
+ },
1089
1114
  {
1090
1115
  name: 'Optimized Capacity',
1091
1116
  type: 'line',
@@ -1124,15 +1149,9 @@
1124
1149
  const timezone = document.getElementById('timezone-select').value;
1125
1150
  renderUsageChart(currentFilteredStartIdx, currentFilteredEndIdx, timezone);
1126
1151
  }
1127
-
1128
- // Display chart data when switching to data tab
1129
- if (button.dataset.tab === 'data') {
1130
- displayChartData();
1131
- }
1132
1152
  });
1133
1153
  });
1134
1154
 
1135
- // Initialize timezone selector
1136
1155
  document.addEventListener('DOMContentLoaded', () => {
1137
1156
  // Detect and display browser timezone
1138
1157
  const browserTimezone = Intl.DateTimeFormat().resolvedOptions().timeZone;
@@ -8,7 +8,7 @@ from hcs_cli.support.scm.html_util import show_html
8
8
 
9
9
 
10
10
  def _timestamp_to_date(timestamp: int):
11
- return datetime.fromtimestamp(timestamp / 1000).strftime("%Y-%m-%dT%H:%M")
11
+ return datetime.utcfromtimestamp(timestamp / 1000).strftime("%Y-%m-%dT%H:%M")
12
12
 
13
13
 
14
14
  def _process_usage_data(usage_api: dict, template_name: str):
@@ -65,12 +65,57 @@ def _process_usage_data(usage_api: dict, template_name: str):
65
65
  optimized_capacity = [0] * n + [0] + optimized_capacity
66
66
  no_spare_error_predicated = [0] * n + [no_spare_error[-1]] + no_spare_error_predicated
67
67
 
68
+ # Calculate predicated powered on vms based on historical data
69
+ # Structure: [zeros for historical period] + [predictions for 7 days (336 slots)]
70
+ # Each week is 336 time slots (48 slots/day * 7 days)
71
+ # Formula: slot[N] = 0.85 * slot[N-336] + 0.15 * (slot[N-336*2] + slot[N-336*3] + slot[N-336*4]) / 3
72
+ powered_on_vms_predicated = []
73
+ total_historical_slots = len(powered_on_vms)
74
+
75
+ # First part: zeros for the historical period
76
+ powered_on_vms_predicated.extend([0] * total_historical_slots)
77
+
78
+ # Second part: 7 days of predictions (336 slots)
79
+ for day_offset in range(7): # 7 days
80
+ for slot_in_day in range(48): # 48 slots per day
81
+ slot_index = day_offset * 48 + slot_in_day
82
+
83
+ # Calculate which slot in the future we're predicting
84
+ # This is relative to the end of historical data
85
+ predict_slot = total_historical_slots + slot_index
86
+
87
+ # Look back to find corresponding slots from previous weeks
88
+ weeks_ago_1 = predict_slot - 336
89
+
90
+ # Get the value from 1 week ago if available
91
+ value_1_week = 0
92
+ if weeks_ago_1 >= 0 and weeks_ago_1 < total_historical_slots:
93
+ value_1_week = powered_on_vms[weeks_ago_1]
94
+
95
+ # Collect values from 2, 3, 4 weeks ago for averaging
96
+ secondary_values = []
97
+ for weeks in [2, 3, 4]:
98
+ weeks_ago = predict_slot - 336 * weeks
99
+ if weeks_ago >= 0 and weeks_ago < total_historical_slots:
100
+ secondary_values.append(powered_on_vms[weeks_ago])
101
+
102
+ # Calculate the average of secondary values
103
+ if secondary_values:
104
+ avg_secondary = sum(secondary_values) / len(secondary_values)
105
+ else:
106
+ avg_secondary = value_1_week
107
+
108
+ # Apply formula: 0.85 * week_1_ago + 0.15 * avg(weeks_2_3_4_ago)
109
+ value = 0.85 * value_1_week + 0.15 * avg_secondary
110
+ powered_on_vms_predicated.append(value)
111
+
68
112
  return {
69
113
  "x_axis": x_axis,
70
114
  "consumed_capacity": consumed_capacity,
71
115
  "spare_capacity": spare_capacity,
72
116
  "no_spare_error": no_spare_error,
73
117
  "powered_on_vms": powered_on_vms,
118
+ "powered_on_vms_predicated": powered_on_vms_predicated,
74
119
  "consumed_capacity_predicated": consumed_capacity_predicated,
75
120
  "spare_capacity_predicated": spare_capacity_predicated,
76
121
  "no_spare_error_predicated": no_spare_error_predicated,
@@ -79,7 +124,7 @@ def _process_usage_data(usage_api: dict, template_name: str):
79
124
  }
80
125
 
81
126
 
82
- def edit_plan(plan: dict, template_name: str, usage_api: dict = None):
127
+ def edit_plan(plan: dict, template_name: str, usage_api: dict = None, template_id: str = None, org_id: str = None):
83
128
  # Start a local server to host plan-editor3.
84
129
  file_path = path.join(path.dirname(__file__), "plan-editor.html.template")
85
130
  with open(file_path, "r") as f:
@@ -132,10 +177,19 @@ def edit_plan(plan: dict, template_name: str, usage_api: dict = None):
132
177
  # Process usage API data for the usage chart
133
178
  usage_chart_data = _process_usage_data(usage_api, template_name)
134
179
 
180
+ # Format the template ID path
181
+ template_id_path = ""
182
+ if org_id and template_id:
183
+ template_id_path = f"org/{org_id}/pool/{template_id}"
184
+ elif template_id:
185
+ template_id_path = template_id
186
+
135
187
  html_template = template.replace(
136
188
  html_template,
137
189
  {
138
190
  "PLAN_DATA": json.dumps(plan),
191
+ "TEMPLATE_NAME": template_name,
192
+ "TEMPLATE_ID_PATH": template_id_path,
139
193
  "x_axis": x_axis,
140
194
  "template_name": template_name,
141
195
  "spare_capacity": spare_capacity,
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: hcs-cli
3
- Version: 0.1.322
3
+ Version: 0.1.323
4
4
  Summary: Horizon Cloud Service CLI.
5
5
  Project-URL: Homepage, https://github.com/euc-eng/hcs-cli
6
6
  Project-URL: Bug Tracker, https://github.com/euc-eng/hcs-cli/issues
@@ -14,7 +14,7 @@ Classifier: License :: OSI Approved :: MIT License
14
14
  Classifier: Operating System :: OS Independent
15
15
  Classifier: Programming Language :: Python :: 3
16
16
  Requires-Python: >=3.9
17
- Requires-Dist: hcs-core>=0.1.322
17
+ Requires-Dist: hcs-core>=0.1.323
18
18
  Requires-Dist: inquirerpy>=0.3.4
19
19
  Requires-Dist: matplotlib>=3.8.0
20
20
  Requires-Dist: paho-mqtt>=2.1.0
@@ -1,4 +1,4 @@
1
- hcs_cli/__init__.py,sha256=n3W6vodFAq8A6X24TsMmyG1uQ7-nArI-TSLG2ueGy_A,72
1
+ hcs_cli/__init__.py,sha256=7KW1VRAYQU035f2jaLhiQJVZNuy6DZMC5son4DF-wQQ,72
2
2
  hcs_cli/__main__.py,sha256=lwPXLjh1OpOs4doVLJJ6_3XHtC-VqDOXiw45hvAQorM,684
3
3
  hcs_cli/main.py,sha256=0NmRcidxNxtJatS_prEKQ4_6bqu-l8UkLhAPOXW3uyc,3537
4
4
  hcs_cli/cmds/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
@@ -235,7 +235,7 @@ hcs_cli/cmds/scm/__init__.py,sha256=qOaaE1aCoIxeA7nRxhI0ZasMEE-KZ9PAfEkTSWdWcHw,
235
235
  hcs_cli/cmds/scm/health.py,sha256=c_0wOh3S4sHBLpAbcjvZGOToRjaEQHlyBD44Ry7pmsI,1277
236
236
  hcs_cli/cmds/scm/operator.py,sha256=9RQPgx0zHUIJLrJnNEu0rUw2VTD73lY-91iYVJ77Pck,3891
237
237
  hcs_cli/cmds/scm/plan.py,sha256=1DauYaO9D8bhBIERNS8WK9k0-BarYN8M8xldGdaHwFQ,16802
238
- hcs_cli/cmds/scm/template.py,sha256=1U8eKap2UTYMqoBvAL5kb26KzCxehfCEiXWy_AVTuKM,1660
238
+ hcs_cli/cmds/scm/template.py,sha256=MkKumBTpWoSz9a-wJMz5TK1wkGO4IDUkSHIxvDacZZM,1717
239
239
  hcs_cli/cmds/site/__init__.py,sha256=f5ZuflhKsBw-8TAJ8xWzlw5lnIU8Imba70GIYSzgz-Q,624
240
240
  hcs_cli/cmds/site/create.py,sha256=jf3g0h7EBf5AW1Di1Qbl62yt6A_sMXGrcKpiZh0gJAQ,1171
241
241
  hcs_cli/cmds/site/delete.py,sha256=7Tq1HaqfsoJxva-XSOa3PKFUKusBUpmg8OkYjwyLlwY,1159
@@ -495,9 +495,9 @@ hcs_cli/support/test_utils2.py,sha256=g321d4lNbJfy8VaSfwWjlj4_ZJe9NW6B16S8GOT3rE
495
495
  hcs_cli/support/use_util.py,sha256=QBrcNIW-k0g41euTc7-oU1WKEmACJx3YGQflyC3YeFs,2970
496
496
  hcs_cli/support/vm_table.py,sha256=hqNUKLVUuBiuDwlqcAQ7uAT1-4z6ZYOIGRcFWn8lnAo,2329
497
497
  hcs_cli/support/scm/html_util.py,sha256=clgMpM90HxRRs3D9ORYYNB57AYh7y_-UzJrB4KX3dsY,458
498
- hcs_cli/support/scm/plan-editor.html.template,sha256=uPbnsDtj4X67iBD8XIlUaSN5IRqwnyt8w6eH2-AnS7c,46021
499
- hcs_cli/support/scm/plan_editor.py,sha256=3SV8mhlrSjdw4UxbfL_FrgSK_aPxnNVqqF7SHD40BlI,6283
500
- hcs_cli-0.1.322.dist-info/METADATA,sha256=wfBQA7qAez3fJZSMlIqDABiaZbM2vdXlrgR6nD1PnCc,3614
501
- hcs_cli-0.1.322.dist-info/WHEEL,sha256=WLgqFyCfm_KASv4WHyYy0P3pM_m7J5L9k2skdKLirC8,87
502
- hcs_cli-0.1.322.dist-info/entry_points.txt,sha256=5uH-af1WUETSBSer2bu4YMGQNY5RriJHsjepb8ACiX8,42
503
- hcs_cli-0.1.322.dist-info/RECORD,,
498
+ hcs_cli/support/scm/plan-editor.html.template,sha256=HtXMmvIvTixVEYN1gblCeqFTt_F8JF6ltDC11Eb_GMw,46975
499
+ hcs_cli/support/scm/plan_editor.py,sha256=TxfHyyozoQ_DCyaPrYXFG9n8AnBuUD8753009FUD0GU,8722
500
+ hcs_cli-0.1.323.dist-info/METADATA,sha256=b-2W82xpVCj8-96cj5dRQJYZuRH3zlCnqPQru2H9hJU,3614
501
+ hcs_cli-0.1.323.dist-info/WHEEL,sha256=WLgqFyCfm_KASv4WHyYy0P3pM_m7J5L9k2skdKLirC8,87
502
+ hcs_cli-0.1.323.dist-info/entry_points.txt,sha256=5uH-af1WUETSBSer2bu4YMGQNY5RriJHsjepb8ACiX8,42
503
+ hcs_cli-0.1.323.dist-info/RECORD,,