hcs-cli 0.1.318__py3-none-any.whl → 0.1.319__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.
Files changed (103) hide show
  1. hcs_cli/__init__.py +1 -1
  2. hcs_cli/cmds/advisor/html_utils.py +30 -26
  3. hcs_cli/cmds/advisor/recommendation_engine.py +7 -10
  4. hcs_cli/cmds/daas/tenant/plan.py +1 -1
  5. hcs_cli/cmds/debug/start.py +0 -1
  6. hcs_cli/cmds/dev/fs/helper/credential_helper.py +2 -0
  7. hcs_cli/cmds/dev/fs/helper/k8s_util.py +0 -1
  8. hcs_cli/cmds/dev/fs/init.py +38 -5
  9. hcs_cli/cmds/dev/fs/profiler.py +0 -1
  10. hcs_cli/cmds/dev/fs/provided_files/akka.plan.yml +94 -250
  11. hcs_cli/cmds/dev/fs/provided_files/azsim.plan.yml +27 -34
  12. hcs_cli/cmds/dev/fs/provided_files/azure.plan.yml +294 -322
  13. hcs_cli/cmds/dev/fs/provided_files/mqtt-secret.yaml +188 -93
  14. hcs_cli/cmds/dev/fs/provided_files/mqtt-server-external.yaml +4 -5
  15. hcs_cli/cmds/dev/fs/provided_files/patch-mqtt-hostname.yml +3 -3
  16. hcs_cli/cmds/dev/fs/provided_files/patch-vernemq-ssl-depth.json +1 -1
  17. hcs_cli/cmds/dev/fs/tailor.py +7 -12
  18. hcs_cli/cmds/dev/mqtt.py +1 -2
  19. hcs_cli/cmds/dev/util/mqtt_helper.py +0 -1
  20. hcs_cli/cmds/hoc/search.py +39 -9
  21. hcs_cli/cmds/hst/clean.py +2 -1
  22. hcs_cli/cmds/inventory/assign.py +1 -3
  23. hcs_cli/cmds/inventory/deassign.py +1 -1
  24. hcs_cli/cmds/scm/plan.py +131 -3
  25. hcs_cli/cmds/task.py +2 -4
  26. hcs_cli/cmds/template/list_usage.py +2 -2
  27. hcs_cli/cmds/template/usage.py +20 -7
  28. hcs_cli/cmds/vm/list.py +0 -1
  29. hcs_cli/config/hcs-deployments.yaml +52 -52
  30. hcs_cli/main.py +0 -2
  31. hcs_cli/payload/akka.blueprint.yml +95 -243
  32. hcs_cli/payload/app/manual.json +19 -19
  33. hcs_cli/payload/edge/akka.json +6 -6
  34. hcs_cli/payload/edge/vsphere.json +6 -6
  35. hcs_cli/payload/hoc/lcm-capcalc.json.template +43 -0
  36. hcs_cli/payload/hoc/no-spare.json.template +1 -1
  37. hcs_cli/payload/inventory/assign.json +14 -16
  38. hcs_cli/payload/inventory/deassign.json +11 -11
  39. hcs_cli/payload/lcm/akka.json +31 -33
  40. hcs_cli/payload/lcm/azure-dummy-nt.json +64 -66
  41. hcs_cli/payload/lcm/azure-dummy.json +64 -66
  42. hcs_cli/payload/lcm/azure-real.json +13 -11
  43. hcs_cli/payload/lcm/edge-proxy.json +34 -36
  44. hcs_cli/payload/lcm/zero-dedicated.json +34 -36
  45. hcs_cli/payload/lcm/zero-delay-1m-per-vm.json +53 -69
  46. hcs_cli/payload/lcm/zero-fail-delete-template.json +43 -0
  47. hcs_cli/payload/lcm/zero-fail-destroy-onthread.json +38 -40
  48. hcs_cli/payload/lcm/zero-fail-destroy.json +38 -40
  49. hcs_cli/payload/lcm/zero-fail-prepare-onthread.json +38 -40
  50. hcs_cli/payload/lcm/zero-fail-prepare.json +38 -40
  51. hcs_cli/payload/lcm/zero-fail-vm-onthread.json +58 -74
  52. hcs_cli/payload/lcm/zero-fail-vm.json +58 -74
  53. hcs_cli/payload/lcm/zero-floating.json +34 -36
  54. hcs_cli/payload/lcm/zero-manual.json +33 -35
  55. hcs_cli/payload/lcm/zero-multisession.json +34 -36
  56. hcs_cli/payload/lcm/zero-nanw.json +31 -33
  57. hcs_cli/payload/lcm/zero-new-5k-delay.json +69 -78
  58. hcs_cli/payload/lcm/zero-new-5k.json +36 -38
  59. hcs_cli/payload/lcm/zero-new-snapshot.json +37 -39
  60. hcs_cli/payload/lcm/zero-new.json +37 -39
  61. hcs_cli/payload/lcm/zero-reuse-vm-id.json +33 -35
  62. hcs_cli/payload/lcm/zero-with-max-id-offset.json +32 -34
  63. hcs_cli/payload/lcm/zero.json +59 -73
  64. hcs_cli/payload/provider/ad-stes-vsphere.json +26 -26
  65. hcs_cli/payload/provider/akka.json +12 -12
  66. hcs_cli/payload/provider/azure.json +14 -14
  67. hcs_cli/payload/provider/edgeproxy.json +12 -12
  68. hcs_cli/payload/provider/vsphere.json +14 -14
  69. hcs_cli/payload/scm/starter.json +22 -23
  70. hcs_cli/payload/synt/core/p01-dummy-success.json +11 -15
  71. hcs_cli/payload/synt/core/p02-dummy-fail.json +12 -15
  72. hcs_cli/payload/synt/core/p03-dummy-exception.json +12 -15
  73. hcs_cli/payload/synt/core/p04-dummy-success-repeat.json +12 -15
  74. hcs_cli/payload/synt/core/p05-dummy-fail-repeat.json +13 -16
  75. hcs_cli/payload/synt/core/p06-dummy-exception-repeat.json +13 -16
  76. hcs_cli/payload/synt/core/p07-dummy-delay.json +12 -15
  77. hcs_cli/payload/synt/core/p08-dummy-property.json +12 -15
  78. hcs_cli/payload/synt/ext/p20-connect-success.json +12 -15
  79. hcs_cli/payload/synt/ext/p21-connect-fail.json +12 -15
  80. hcs_cli/payload/synt/ext/p30-ssl-success.json +12 -15
  81. hcs_cli/payload/synt/ext/p31-ssl-fail.json +13 -16
  82. hcs_cli/payload/synt/ext/p40-http-success.json +12 -15
  83. hcs_cli/payload/synt/ext/p41-http-fail.json +12 -15
  84. hcs_cli/payload/synt/ext/p42-http-status-code.json +14 -20
  85. hcs_cli/payload/synt/ext1/p10-ping-success.json +13 -16
  86. hcs_cli/payload/synt/ext1/p11-ping-fail.json +12 -15
  87. hcs_cli/payload/synt/ext1/p12-ping-success-repeat.json +14 -17
  88. hcs_cli/provider/hcs/cert.py +0 -1
  89. hcs_cli/provider/hcs/edge.py +1 -1
  90. hcs_cli/provider/hcs/uag.py +1 -1
  91. hcs_cli/service/hoc/diagnostic.py +0 -3
  92. hcs_cli/service/lcm/vm.py +0 -1
  93. hcs_cli/service/task.py +0 -1
  94. hcs_cli/support/debug_util.py +0 -1
  95. hcs_cli/support/plan_util.py +0 -1
  96. hcs_cli/support/predefined_payload.py +4 -1
  97. hcs_cli/support/template_util.py +0 -1
  98. hcs_cli/support/test_utils.py +2 -2
  99. hcs_cli/support/test_utils2.py +536 -0
  100. {hcs_cli-0.1.318.dist-info → hcs_cli-0.1.319.dist-info}/METADATA +24 -17
  101. {hcs_cli-0.1.318.dist-info → hcs_cli-0.1.319.dist-info}/RECORD +103 -100
  102. {hcs_cli-0.1.318.dist-info → hcs_cli-0.1.319.dist-info}/WHEEL +0 -0
  103. {hcs_cli-0.1.318.dist-info → hcs_cli-0.1.319.dist-info}/entry_points.txt +0 -0
hcs_cli/__init__.py CHANGED
@@ -1,3 +1,3 @@
1
- __version__ = "0.1.318"
1
+ __version__ = "0.1.319"
2
2
 
3
3
  from . import service
@@ -131,10 +131,10 @@ def prepare_vm_utilization_chart_data(template_info: dict) -> dict:
131
131
  utilized_capacity.append(0)
132
132
 
133
133
  start_date = datetime.fromtimestamp(start_timestamp / 1000)
134
- x_axis = [start_date + timedelta(minutes=(timeslot_ms / (1000 * 60)) * i) for i in range(data_length)]
134
+ x_axis_dt = [start_date + timedelta(minutes=(timeslot_ms / (1000 * 60)) * i) for i in range(data_length)]
135
135
 
136
136
  # Convert datetime objects to formatted strings for ECharts
137
- x_axis = [dt.strftime("%Y-%m-%d %H:%M") for dt in x_axis]
137
+ x_axis = [dt.strftime("%Y-%m-%d %H:%M") for dt in x_axis_dt]
138
138
  else:
139
139
  x_axis = []
140
140
  provisioned_vms = []
@@ -287,7 +287,7 @@ def _get_pool_html_template(
287
287
  itemStyle: {{
288
288
  color: '#007acc'
289
289
  }},
290
- data: {chart_data['provisioned_vms']}
290
+ data: {chart_data["provisioned_vms"]}
291
291
  }},
292
292
  {{
293
293
  name: 'Powered-on VMs',
@@ -303,7 +303,7 @@ def _get_pool_html_template(
303
303
  itemStyle: {{
304
304
  color: '#dc3545'
305
305
  }},
306
- data: {chart_data['powered_on_vms']}
306
+ data: {chart_data["powered_on_vms"]}
307
307
  }},
308
308
  {{
309
309
  name: 'Utilized Capacity',
@@ -319,7 +319,7 @@ def _get_pool_html_template(
319
319
  itemStyle: {{
320
320
  color: '#28a745'
321
321
  }},
322
- data: {chart_data['utilized_capacity']}
322
+ data: {chart_data["utilized_capacity"]}
323
323
  }}
324
324
  """
325
325
  else: # line chart
@@ -332,7 +332,7 @@ def _get_pool_html_template(
332
332
  focus: 'series'
333
333
  }},
334
334
  color: '#007acc',
335
- data: {chart_data['provisioned_vms']}
335
+ data: {chart_data["provisioned_vms"]}
336
336
  }},
337
337
  {{
338
338
  name: 'Powered-on VMs',
@@ -342,7 +342,7 @@ def _get_pool_html_template(
342
342
  focus: 'series'
343
343
  }},
344
344
  color: '#dc3545',
345
- data: {chart_data['powered_on_vms']}
345
+ data: {chart_data["powered_on_vms"]}
346
346
  }},
347
347
  {{
348
348
  name: 'Utilized Capacity',
@@ -352,7 +352,7 @@ def _get_pool_html_template(
352
352
  focus: 'series'
353
353
  }},
354
354
  color: '#28a745',
355
- data: {chart_data['utilized_capacity']}
355
+ data: {chart_data["utilized_capacity"]}
356
356
  }}
357
357
  """
358
358
 
@@ -557,19 +557,21 @@ def _get_pool_html_template(
557
557
  </html>
558
558
  """
559
559
 
560
- return yumako.template.replace(
561
- template,
562
- {
563
- "org_name": org_name,
564
- "resource_type": resource_type,
565
- "resource_id": resource_id,
566
- "generation_date": generation_date,
567
- "summary_html": summary_html,
568
- "usage_table_html": usage_table_html,
569
- "recommendations_html": recommendations_html,
570
- "x_axis_data": chart_data["x_axis"],
571
- "chart_series_data": chart_series,
572
- },
560
+ return str(
561
+ yumako.template.replace(
562
+ template,
563
+ {
564
+ "org_name": org_name,
565
+ "resource_type": resource_type,
566
+ "resource_id": resource_id,
567
+ "generation_date": generation_date,
568
+ "summary_html": summary_html,
569
+ "usage_table_html": usage_table_html,
570
+ "recommendations_html": recommendations_html,
571
+ "x_axis_data": chart_data["x_axis"],
572
+ "chart_series_data": chart_series,
573
+ },
574
+ )
573
575
  )
574
576
 
575
577
 
@@ -600,8 +602,8 @@ def _get_org_html_template(org_name: str, generation_date: str, all_recommendati
600
602
  recommended_settings = json.dumps(rec["recommended_settings"], indent=2)
601
603
  recommendations_html += f"""
602
604
  <tr>
603
- <td>{rec['action']}</td>
604
- <td>{rec['justification']}</td>
605
+ <td>{rec["action"]}</td>
606
+ <td>{rec["justification"]}</td>
605
607
  <td><pre>{current_settings}</pre></td>
606
608
  <td><pre>{recommended_settings}</pre></td>
607
609
  </tr>
@@ -709,7 +711,9 @@ def _get_org_html_template(org_name: str, generation_date: str, all_recommendati
709
711
  </html>
710
712
  """
711
713
 
712
- return yumako.template.replace(
713
- template,
714
- {"org_name": org_name, "generation_date": generation_date, "recommendations_html": recommendations_html},
714
+ return str(
715
+ yumako.template.replace(
716
+ template,
717
+ {"org_name": org_name, "generation_date": generation_date, "recommendations_html": recommendations_html},
718
+ )
715
719
  )
@@ -1,8 +1,6 @@
1
1
  """
2
2
  Copyright © 2025 Omnissa, LLC.
3
- """
4
3
 
5
- """
6
4
  Recommendation engine for generating recommendations for advisor reports.
7
5
  """
8
6
 
@@ -137,7 +135,7 @@ def calculate_observation_period_days(template_info: dict) -> float:
137
135
  observation_hours = (data_length_after_cutoff * timeslot_ms) / (1000 * 60 * 60)
138
136
  observation_days = observation_hours / 24
139
137
 
140
- return max(1.0, observation_days) # Minimum 1 day
138
+ return float(max(1.0, observation_days)) # Minimum 1 day
141
139
  except Exception:
142
140
  return 30.0 # Default to 30 days
143
141
 
@@ -259,7 +257,7 @@ def calculate_peak_utilized_capacity(template_info: dict) -> int:
259
257
  else:
260
258
  peak_capacity = peak_sessions
261
259
 
262
- return peak_capacity
260
+ return int(peak_capacity)
263
261
  except Exception:
264
262
  return 0
265
263
 
@@ -377,7 +375,7 @@ def estimate_cost_savings_max_vms_reduction(old_max_vms: int, new_max_vms: int,
377
375
  # Total savings = VM savings + disk savings
378
376
  total_savings = vm_savings + disk_savings
379
377
 
380
- return round(total_savings, 2)
378
+ return float(round(total_savings, 2))
381
379
  except Exception:
382
380
  return 0.0
383
381
 
@@ -478,7 +476,7 @@ def calculate_scheduled_powered_on_hours(template_info: dict, power_schedules: l
478
476
  # Calculate total powered-on hours during scheduled periods
479
477
  total_scheduled_hours = sum(scheduled_powered_on) * timeslot_hours
480
478
 
481
- return total_scheduled_hours
479
+ return float(total_scheduled_hours)
482
480
 
483
481
  except Exception as e:
484
482
  print(f"Warning: Error calculating scheduled powered-on hours: {str(e)}")
@@ -658,7 +656,7 @@ def estimate_cost_savings_min_available_vms(old_min: float, new_min: float, temp
658
656
 
659
657
  # Calculate savings
660
658
  savings = limit * (old_min - new_min) * config.HOURS_PER_MONTH * vm_cost_per_hour
661
- return round(savings, 2)
659
+ return float(round(savings, 2))
662
660
  except Exception:
663
661
  return 0.0
664
662
 
@@ -1067,7 +1065,7 @@ def calculate_schedule_resource_utilization(template_info: dict, power_schedules
1067
1065
  # Calculate utilization percentage
1068
1066
  if scheduled_allocated_hours > 0:
1069
1067
  utilization = (scheduled_utilized_hours / scheduled_allocated_hours) * 100
1070
- return max(0.0, utilization)
1068
+ return float(max(0.0, utilization))
1071
1069
 
1072
1070
  return 0.0
1073
1071
 
@@ -1088,7 +1086,6 @@ def _is_within_scheduled_period(point_datetime, power_schedules):
1088
1086
  bool: True if point falls within any scheduled period
1089
1087
  """
1090
1088
  try:
1091
-
1092
1089
  # Get day of week (0=Monday, 6=Sunday)
1093
1090
  day_of_week = point_datetime.weekday()
1094
1091
  # Convert to our bit mask format (0=Monday, 6=Sunday)
@@ -1486,7 +1483,7 @@ def is_all_at_once_provisioning(template_info: dict) -> bool:
1486
1483
  min_vms = spare_policy.get("min", 0)
1487
1484
 
1488
1485
  # "All at once" if limit == max == min
1489
- return limit > 0 and limit == max_vms and max_vms == min_vms
1486
+ return bool(limit > 0 and limit == max_vms and max_vms == min_vms)
1490
1487
  except Exception:
1491
1488
  return False
1492
1489
 
@@ -90,7 +90,7 @@ def _select_order_type(var: dict):
90
90
  def fn_get_text(order_type: str):
91
91
  o = orders[order_type]
92
92
  num_apps = len(o["application"]["info"])
93
- desc = f'{o["template"]["type"]}/{o["image"]["os"]},{o["image"]["gen"]}, apps: {num_apps}'
93
+ desc = f"{o['template']['type']}/{o['image']['os']},{o['image']['gen']}, apps: {num_apps}"
94
94
  ret = f"{order_type} ({desc})"
95
95
  return ret
96
96
 
@@ -59,7 +59,6 @@ def pod_forwarding(pod_id):
59
59
 
60
60
 
61
61
  def run_with_debug(pod_id):
62
-
63
62
  run_app_command = f"kubectl exec -it {pod_id} -c app -- /bin/bash -c 'java -agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=5005 -jar app.jar'"
64
63
 
65
64
  # Execute the command
@@ -12,6 +12,8 @@ def _get_application_properties_from_configmap(configmap_name: str):
12
12
  log.warn(f"ConfigMap {configmap_name} not found.")
13
13
  return {}
14
14
  app_properties_text = config["data"].get("application.properties")
15
+ if app_properties_text is None:
16
+ app_properties_text = config["data"].get("application-group.properties")
15
17
  if app_properties_text is not None:
16
18
  lines = app_properties_text.split("\n")
17
19
  app_properties = {}
@@ -35,7 +35,6 @@ def kubectl(command: str, ignore_error: bool = False, get_json: bool = False, in
35
35
 
36
36
 
37
37
  def validate_kubeconfig(fs_name: str, try_default_config: bool = True, raise_on_error: bool = True):
38
-
39
38
  if os.path.exists(_fs_kubeconfig):
40
39
  log.info("Using feature stack kubeconfig: " + _fs_kubeconfig)
41
40
  global _kubectl_command
@@ -17,6 +17,7 @@ import inspect
17
17
  import json
18
18
  import os
19
19
  import re
20
+ import tempfile
20
21
  import shutil
21
22
  import subprocess
22
23
  import time
@@ -25,6 +26,7 @@ from pathlib import Path
25
26
 
26
27
  import click
27
28
  import hcs_core.sglib.cli_options as cli
29
+ import yaml
28
30
  from dotenv import load_dotenv
29
31
  from hcs_core.ctxp import context, profile
30
32
  from hcs_core.ctxp.util import error_details
@@ -290,8 +292,34 @@ def _create_infra_azure(fs_name):
290
292
 
291
293
  @step
292
294
  def _create_infra_azsim():
293
- azusim_plan_path = _resolve_bundled_file_path("provided_files/azsim.plan.yml")
294
- ret = run_cli("hcs plan apply -f " + azusim_plan_path, raise_on_error=False)
295
+ import requests
296
+
297
+ # Fetch credentials from simhub
298
+ SIMHUB_URL = "https://simhub.steslabs.net/subscription"
299
+ try:
300
+ resp = requests.get(SIMHUB_URL)
301
+ resp.raise_for_status()
302
+ creds = resp.json()
303
+ except Exception as e:
304
+ log.warn(f"Failed to fetch simhub credentials: {e}")
305
+ return
306
+ log.info(f"Azure Simulator credentials fetched from simhub: {creds}")
307
+ # Read the plan template
308
+ plan_template_path = _resolve_bundled_file_path("provided_files/azsim.plan.yml")
309
+ with open(plan_template_path, "r") as f:
310
+ plan = yaml.safe_load(f)
311
+
312
+ # Overwrite provider values
313
+ plan["var"]["provider"]["subscriptionId"] = creds["subscriptionId"]
314
+
315
+ # Write to a temp file
316
+ tmp = tempfile.NamedTemporaryFile(delete=False, suffix=".yml", mode="w")
317
+ yaml.dump(plan, tmp)
318
+ tmp.close()
319
+ dynamic_plan_path = tmp.name
320
+
321
+ # Apply the plan
322
+ ret = run_cli("hcs plan apply -f " + dynamic_plan_path, raise_on_error=False)
295
323
  if ret.returncode == 0:
296
324
  log.good("Azure simulator infrastructure set up.")
297
325
  else:
@@ -469,8 +497,8 @@ def _write_env_file(data, props, env_file):
469
497
  for key, value in data.items():
470
498
  if key not in ignore_keys:
471
499
  f.write(f"{to_env_key(key)}={value}\n")
472
- config_block = f"""HCS_CLIENT_ID={props['csp.service.app.client_id']}
473
- HCS_CLIENT_SECRET={props['csp.service.app.client_secret']}
500
+ config_block = f"""HCS_CLIENT_ID={props["csp.service.app.client_id"]}
501
+ HCS_CLIENT_SECRET={props["csp.service.app.client_secret"]}
474
502
  IDP_TENANT_DOMAIN=test-sanity-domain
475
503
  UAG_FQDN=myuag.fqdn
476
504
  DESKTOP_SUBNET_CIDR=10.76.0.0/16
@@ -1016,12 +1044,17 @@ def _create_idp():
1016
1044
  log.good("IDP set up.")
1017
1045
  except Exception as e:
1018
1046
  print(e)
1019
- fail("IDP credentials require 2FA which is currently not supported. Please manually set up your IDP in Astro.")
1047
+ fail(
1048
+ "IDP credentials require 2FA, which is currently not supported via API calls. Please manually set up "
1049
+ "your IDP in Astro. Once you have completed set up, re-run the 'hcs dev fs init' command skipping the"
1050
+ "Common Init step."
1051
+ )
1020
1052
 
1021
1053
 
1022
1054
  @step
1023
1055
  def _restart_services():
1024
1056
  kubectl("rollout restart deployment portal-deployment", ignore_error=True)
1057
+ kubectl("rollout restart deployment infra-vsphere-twin-deployment", ignore_error=True)
1025
1058
  kubectl("rollout restart statefulset vmhub-statefulset", ignore_error=True)
1026
1059
  kubectl("rollout restart statefulset connection-service-statefulset", ignore_error=True)
1027
1060
  kubectl("rollout restart statefulset clouddriver-statefulset", ignore_error=True)
@@ -65,7 +65,6 @@ def _run_mongo_commands(command: str):
65
65
 
66
66
 
67
67
  def _enable_mongo_profiler():
68
-
69
68
  mongo_commands = """
70
69
  use app
71
70
  db.setProfilingLevel(0)