iotedgedev 3.3.5__zip → 3.3.7__zip

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 (79) hide show
  1. s/.git/FETCH_HEAD +3 -1
  2. s/.git/HEAD +1 -1
  3. s/.git/index +0 -0
  4. s/.git/logs/HEAD +1 -1
  5. s/.git/logs/refs/remotes/origin/EO +1 -1
  6. s/.git/logs/refs/remotes/origin/avagraw/security_fix +1 -1
  7. s/.git/logs/refs/remotes/origin/cherry-pick-rename +1 -1
  8. s/.git/logs/refs/remotes/origin/chyuan/fix-validation-not-fail-test +1 -1
  9. s/.git/logs/refs/remotes/origin/chyuan/update-linux-agent +1 -1
  10. s/.git/logs/refs/remotes/origin/dependabot/pip/cookiecutter-2.1.1 +1 -1
  11. s/.git/logs/refs/remotes/origin/dependabot/pip/pyyaml-5.4 +1 -1
  12. s/.git/logs/refs/remotes/origin/main +1 -1
  13. s/.git/logs/refs/remotes/origin/ray-deps +1 -1
  14. s/.git/logs/refs/remotes/origin/ray-dev-setup +1 -1
  15. s/.git/logs/refs/remotes/origin/ray-tox +1 -1
  16. s/.git/logs/refs/remotes/origin/release-pipeline-automation +1 -1
  17. s/.git/logs/refs/remotes/origin/revert-594-user/vikas_bug-pyopenssl +1 -0
  18. s/.git/logs/refs/remotes/origin/spmcdono/fix-pipeline-test-install +1 -1
  19. s/.git/logs/refs/remotes/origin/spmcdono/layered-deployments +1 -1
  20. s/.git/logs/refs/remotes/origin/spmcdono/pypi-package-test-pipeline +1 -1
  21. s/.git/logs/refs/remotes/origin/spmcdono/remove-py2 +1 -1
  22. s/.git/logs/refs/remotes/origin/spmcdono/remove-py2-dev +1 -1
  23. s/.git/logs/refs/remotes/origin/spmcdono/test-native-runtime-install +1 -1
  24. s/.git/logs/refs/remotes/origin/spmcdono/update-pipeline +1 -1
  25. s/.git/logs/refs/remotes/origin/user/marine/EliiseS/devcontainer +1 -1
  26. s/.git/logs/refs/remotes/origin/user/marine/fixsimver +1 -1
  27. s/.git/logs/refs/remotes/origin/user/marine/layereddeploymentsbuildtest +1 -1
  28. s/.git/logs/refs/remotes/origin/user/marine/master-to-main +1 -1
  29. s/.git/logs/refs/remotes/origin/user/marine/pipeline +1 -1
  30. s/.git/logs/refs/remotes/origin/user/marine/updatesimver +1 -0
  31. s/.git/logs/refs/remotes/origin/user/vikas +1 -1
  32. s/.git/logs/refs/remotes/origin/user/vikas-bud-fix +1 -0
  33. s/.git/logs/refs/remotes/origin/user/vikas0212/verify-tests +1 -1
  34. s/.git/logs/refs/remotes/origin/user/vikas2 +1 -1
  35. s/.git/logs/refs/remotes/origin/user/vikas_bug-pyopenssl +1 -0
  36. s/.git/logs/refs/remotes/origin/user/vikas_pypi +1 -1
  37. s/.git/logs/refs/remotes/origin/user/vikas_pypi-1 +1 -1
  38. s/.git/logs/refs/remotes/origin/user/vikas_supplychain-issue1 +1 -1
  39. s/.git/logs/refs/remotes/origin/user/vikas_testing +1 -1
  40. s/.git/logs/refs/remotes/origin/user/vikas_trigger +1 -1
  41. s/.git/logs/refs/remotes/origin/user/vikas_trigger_new +1 -1
  42. s/.git/logs/refs/remotes/origin/user/xinzed/dps_conn_str +1 -1
  43. s/.git/logs/refs/remotes/origin/user/xinzed/update-python-docker +1 -1
  44. s/.git/logs/refs/remotes/origin/vikas/azure +1 -1
  45. s/.git/logs/refs/remotes/origin/vikas/dependebot +1 -1
  46. s/.git/logs/refs/remotes/origin/vikas-triggers +1 -1
  47. s/.git/logs/refs/remotes/origin/xinzed/3_1_0rc1 +1 -1
  48. s/.git/logs/refs/remotes/origin/xinzed/update-runtime +1 -1
  49. s/.git/objects/pack/{pack-7a60b35c219082b6cfceccd45f3712d11b9c0e3b.idx → pack-a04ba9522e6fa097b45076bd52c4ffab33f906ac.idx} +0 -0
  50. s/.git/objects/pack/{pack-7a60b35c219082b6cfceccd45f3712d11b9c0e3b.pack → pack-a04ba9522e6fa097b45076bd52c4ffab33f906ac.pack} +0 -0
  51. s/.git/refs/remotes/origin/dependabot/pip/cookiecutter-2.1.1 +1 -1
  52. s/.git/refs/remotes/origin/main +1 -1
  53. s/.git/refs/remotes/origin/revert-594-user/vikas_bug-pyopenssl +1 -0
  54. s/.git/refs/remotes/origin/user/marine/updatesimver +1 -0
  55. s/.git/refs/remotes/origin/user/vikas-bud-fix +1 -0
  56. s/.git/refs/remotes/origin/user/vikas_bug-pyopenssl +1 -0
  57. s/.git/refs/tags/v3.3.6 +1 -0
  58. s/.git/refs/tags/v3.3.7 +1 -0
  59. s/CHANGELOG.md +9 -0
  60. s/build/lib/iotedgedev/__init__.py +1 -1
  61. s/build/lib/iotedgedev/azurecli.py +66 -18
  62. s/build/lib/iotedgedev/cli.py +75 -40
  63. s/build/lib/iotedgedev/envvars.py +1 -0
  64. s/build/lib/iotedgedev/output.py +4 -0
  65. s/dist/{iotedgedev-3.3.5-py3-none-any.whl → iotedgedev-3.3.7-py3-none-any.whl} +0 -0
  66. s/docs/command-list.md +1 -0
  67. s/docs/environment-setup/manual-dev-machine-setup.md +1 -1
  68. s/docs/quickstart.md +2 -2
  69. s/iotedgedev/__init__.py +1 -1
  70. s/iotedgedev/azurecli.py +66 -18
  71. s/iotedgedev/cli.py +75 -40
  72. s/iotedgedev/envvars.py +1 -0
  73. s/iotedgedev/output.py +4 -0
  74. s/iotedgedev.egg-info/PKG-INFO +1 -1
  75. s/iotedgedev.egg-info/requires.txt +1 -1
  76. s/requirements.txt +1 -1
  77. s/setup.cfg +1 -1
  78. s/setup.py +2 -2
  79. s/tests/__init__.py +1 -1
@@ -25,11 +25,17 @@ from .solution import Solution
25
25
  from .utility import Utility
26
26
 
27
27
  # Extend click.Group#add_command(cmd, name) to take deprecated=bool flag by monkey patching.
28
- def add_command_with_deprecation(self, cmd: Command, name: t.Optional[str] = None, deprecated = False):
28
+ def add_command_with_deprecation(self, cmd: Command, name: t.Optional[str] = None, deprecated = False, alt: t.Optional[str] = None):
29
29
  # Directly changing the deprecated flag of the cmd has side-effects on the reference source.
30
30
  # To avoid this, copy the cmd object with deepcopy.
31
31
  copied_command = copy.deepcopy(cmd)
32
32
  copied_command.deprecated = deprecated
33
+ if copied_command.short_help is None:
34
+ copied_command.short_help = copied_command.help
35
+ if alt is not None:
36
+ alt_cmd = "[Use `iotedgedev " + alt + "` instead]"
37
+ copied_command.short_help += " " + alt_cmd # use space for formatting short_help in `iotedgedev --help`
38
+ copied_command.help += "\n\n" + alt_cmd # use newline for formatting help in `iotedgedev <command> --help`
33
39
  self.add_command(copied_command, name)
34
40
 
35
41
  Group.add_command_with_deprecation = add_command_with_deprecation
@@ -99,7 +105,7 @@ def new(name, module, template, edge_runtime_version, group_id):
99
105
  sol = Solution(output, utility)
100
106
  sol.create(name, module, template, edge_runtime_version, group_id)
101
107
 
102
- main.add_command_with_deprecation(new, deprecated=True)
108
+ main.add_command_with_deprecation(new, deprecated=True, alt="solution new")
103
109
 
104
110
 
105
111
  @solution.command(context_settings=CONTEXT_SETTINGS,
@@ -132,7 +138,7 @@ def init(name, module, template, group_id, edge_runtime_version):
132
138
  utility.call_proc(azsetupcmd.split())
133
139
 
134
140
 
135
- main.add_command_with_deprecation(init, deprecated=True)
141
+ main.add_command_with_deprecation(init, deprecated=True, alt="solution init")
136
142
 
137
143
 
138
144
  @solution.command(context_settings=CONTEXT_SETTINGS, help="Push, deploy, start, monitor")
@@ -155,7 +161,7 @@ def add(name, template, group_id):
155
161
  mod.add(name, template, group_id)
156
162
 
157
163
 
158
- main.add_command_with_deprecation(add, deprecated=True)
164
+ main.add_command_with_deprecation(add, deprecated=True, alt="solution add")
159
165
 
160
166
 
161
167
  @solution.command(context_settings=CONTEXT_SETTINGS, help="Build the solution")
@@ -197,7 +203,7 @@ def build(ctx, push, do_deploy, template_file, platform):
197
203
  ctx.invoke(deploy)
198
204
 
199
205
 
200
- main.add_command_with_deprecation(build, deprecated=True)
206
+ main.add_command_with_deprecation(build, deprecated=True, alt="solution build")
201
207
 
202
208
 
203
209
  @solution.command(context_settings=CONTEXT_SETTINGS, help="Push module images to container registry")
@@ -238,7 +244,7 @@ def push(ctx, do_deploy, no_build, template_file, platform):
238
244
  ctx.invoke(deploy)
239
245
 
240
246
 
241
- main.add_command_with_deprecation(push, deprecated=True)
247
+ main.add_command_with_deprecation(push, deprecated=True, alt="solution push")
242
248
 
243
249
 
244
250
  @solution.command(context_settings=CONTEXT_SETTINGS, help="Deploy solution to IoT Edge device")
@@ -256,7 +262,7 @@ def deploy(manifest_file):
256
262
  edge.deploy(manifest_file)
257
263
 
258
264
 
259
- main.add_command_with_deprecation(deploy, deprecated=True)
265
+ main.add_command_with_deprecation(deploy, deprecated=True, alt="solution deploy")
260
266
 
261
267
 
262
268
  @iothub.command(
@@ -351,7 +357,7 @@ def genconfig(template_file, platform, fail_on_validation_error):
351
357
  mod.build_push(template_file, platform, no_build=True, no_push=True, fail_on_validation_error=fail_on_validation_error)
352
358
 
353
359
 
354
- main.add_command_with_deprecation(genconfig, deprecated=True)
360
+ main.add_command_with_deprecation(genconfig, deprecated=True, alt="solution genconfig")
355
361
 
356
362
 
357
363
  @simulator.command(context_settings=CONTEXT_SETTINGS,
@@ -374,7 +380,7 @@ def setup_simulator(gateway_host, iothub_connection_string):
374
380
  sim.setup(gateway_host, iothub_connection_string)
375
381
 
376
382
 
377
- main.add_command_with_deprecation(setup_simulator, deprecated=True)
383
+ main.add_command_with_deprecation(setup_simulator, deprecated=True, alt="simulator setup")
378
384
 
379
385
 
380
386
  @simulator.command(context_settings=CONTEXT_SETTINGS,
@@ -449,7 +455,7 @@ def start_simulator(setup, solution, build, manifest_file, platform, verbose, in
449
455
  sim.start_single(inputs, port)
450
456
 
451
457
 
452
- main.add_command_with_deprecation(start_simulator, deprecated=True)
458
+ main.add_command_with_deprecation(start_simulator, deprecated=True, alt="simulator start")
453
459
 
454
460
 
455
461
  @simulator.command(context_settings=CONTEXT_SETTINGS,
@@ -461,7 +467,7 @@ def stop_simulator():
461
467
  sim.stop()
462
468
 
463
469
 
464
- main.add_command_with_deprecation(stop_simulator, deprecated=True)
470
+ main.add_command_with_deprecation(stop_simulator, deprecated=True, alt="simulator stop")
465
471
 
466
472
 
467
473
  @simulator.command(context_settings=CONTEXT_SETTINGS,
@@ -501,7 +507,7 @@ def monitor(timeout):
501
507
  ih.monitor_events(timeout)
502
508
 
503
509
 
504
- main.add_command_with_deprecation(monitor, deprecated=True)
510
+ main.add_command_with_deprecation(monitor, deprecated=True, alt="iothub monitor")
505
511
 
506
512
 
507
513
  def ensure_azure_cli_iot_ext():
@@ -622,25 +628,31 @@ def validate_option(ctx, param, value):
622
628
  def list_edge_devices_and_set_default():
623
629
  if not azure_cli.list_edge_devices(envvars.IOTHUB_NAME):
624
630
  sys.exit()
625
- return "iotedgedev-edgedevice"
631
+ output.info("Enter the IoT Edge Device Id (Creates a new Edge Device if not found) [iotedgedev-edgedevice]:")
632
+ return input() or "iotedgedev-edgedevice"
626
633
 
627
634
 
628
635
  def list_iot_hubs_and_set_default():
629
636
  if not azure_cli.list_iot_hubs(envvars.RESOURCE_GROUP_NAME):
630
637
  sys.exit()
631
638
 
639
+ subscription_rg_hash = hashlib.sha1((default_subscriptionId + envvars.RESOURCE_GROUP_NAME).encode('utf-8')).hexdigest()[:6]
640
+ iothub_name = "iotedgedev-iothub-" + subscription_rg_hash
641
+
632
642
  first_iothub = azure_cli.get_first_iothub(envvars.RESOURCE_GROUP_NAME)
633
643
  if first_iothub:
634
- return first_iothub
635
- else:
636
- subscription_rg_hash = hashlib.sha1((default_subscriptionId + envvars.RESOURCE_GROUP_NAME).encode('utf-8')).hexdigest()[:6]
637
- return "iotedgedev-iothub-" + subscription_rg_hash
644
+ iothub_name = first_iothub
645
+
646
+ output.info(f"Enter the IoT Hub Name (Creates a new IoT Hub if not found) [{iothub_name}]:")
647
+ return input() or iothub_name
638
648
 
639
649
 
640
650
  def list_resource_groups_and_set_default():
641
651
  if not azure_cli.list_resource_groups():
642
652
  sys.exit()
643
- return "iotedgedev-rg"
653
+
654
+ output.info("Enter Resource Group Name (Creates a new Resource Group if not found) [iotedgedev-rg]:")
655
+ return input() or "iotedgedev-rg"
644
656
 
645
657
 
646
658
  def list_subscriptions_and_set_default():
@@ -657,14 +669,30 @@ def list_subscriptions_and_set_default():
657
669
  if not azure_cli.list_subscriptions():
658
670
  sys.exit()
659
671
  default_subscriptionId = azure_cli.get_default_subscription()
660
- return default_subscriptionId
672
+ output.info(f"Select an Azure Subscription Name or Id [{default_subscriptionId}]:")
673
+ return input() or default_subscriptionId
661
674
 
662
675
 
663
- def header_and_default(header, default, default2=None):
676
+ def header_and_default(header, prompt, default, options=None):
664
677
  output.header(header)
665
- if default == '' and default2 is not None:
666
- return default2
667
- return default
678
+ output.info(f"{prompt} [{default}]:")
679
+ output.info(f"Available options: {options}")
680
+ return input() or default
681
+
682
+
683
+ def handle_vm_create():
684
+ output.header("CREATE VIRTUAL MACHINE")
685
+ output.info("The virtual machine will be created with the IoT Edge runtime installed.\n"
686
+ "The deployment template can be found at: https://aka.ms/iotedge-vm-deploy \n"
687
+ "The name of the virtual machine will be a unique string generated by the template.\n"
688
+ "Details: os=ubuntu20_04-lts, size=Standard_DS1_v2, username=azureuser, allowSsh=true")
689
+ ssh_key_file = output.prompt_question("SSH Key file (must be located in ~/.ssh and will be created if it doesn't exist)", "id_rsa_iotedgedev")
690
+ if not azure_cli.create_edge_vm(ssh_key_file, envvars.EDGE_DEVICE_ID, envvars.IOTHUB_NAME, envvars.RESOURCE_GROUP_NAME):
691
+ raise click.BadParameter(f('Could not create Virtual Machine in {envvars.RESOURCE_GROUP_NAME}'))
692
+ else:
693
+ output.info("Virtual Machine created successfully. Check Azure Portal for details on how to connect.\n"
694
+ "WARNING: Check the details of the Network Security Group, it is setup to allow SSH from all addresses.\n"
695
+ "You will be able to connect using the SSH Key file: ~/.ssh/" + ssh_key_file)
668
696
 
669
697
 
670
698
  @iothub.command(context_settings=CONTEXT_SETTINGS,
@@ -688,51 +716,46 @@ def header_and_default(header, default, default2=None):
688
716
  help="Enter Azure Service Principal Credentials (username password tenant).")
689
717
  @click.option('--subscription',
690
718
  envvar=envvars.get_envvar_key_if_val("SUBSCRIPTION_ID"),
691
- default=lambda: list_subscriptions_and_set_default(),
719
+ default=list_subscriptions_and_set_default,
692
720
  required=True,
693
721
  callback=validate_option,
694
- prompt="Select an Azure Subscription Name or Id:",
695
722
  help="The Azure Subscription Name or Id.")
696
723
  @click.option('--resource-group-location',
697
724
  envvar=envvars.get_envvar_key_if_val("RESOURCE_GROUP_LOCATION"),
698
725
  required=True,
699
- default=lambda: header_and_default('RESOURCE GROUP LOCATION', envvars.RESOURCE_GROUP_LOCATION, 'westus'),
700
- type=click.Choice(['australiaeast', 'australiasoutheast', 'brazilsouth', 'canadacentral', 'canadaeast', 'centralindia', 'centralus', 'eastasia', 'eastus', 'eastus2',
701
- 'japanwest', 'japaneast', 'northeurope', 'northcentralus', 'southindia', 'uksouth', 'ukwest', 'westus', 'westeurope', 'southcentralus', 'westcentralus', 'westus2']),
726
+ default=lambda: header_and_default('RESOURCE GROUP LOCATION', 'Enter a Resource Group Location', envvars.RESOURCE_GROUP_LOCATION or 'westus',
727
+ ['australiaeast', 'australiasoutheast', 'brazilsouth', 'canadacentral', 'canadaeast', 'centralindia', 'centralus', 'eastasia', 'eastus', 'eastus2',
728
+ 'japanwest', 'japaneast', 'northeurope', 'northcentralus', 'southindia', 'uksouth', 'ukwest', 'westus', 'westeurope', 'southcentralus', 'westcentralus', 'westus2']),
729
+ type=str,
702
730
  callback=validate_option,
703
- prompt="Enter a Resource Group Location:",
704
731
  help="The Resource Group Location.")
705
732
  @click.option('--resource-group-name',
706
733
  envvar=envvars.get_envvar_key_if_val("RESOURCE_GROUP_NAME"),
707
734
  required=True,
708
- default=lambda: list_resource_groups_and_set_default(),
735
+ default=list_resource_groups_and_set_default,
709
736
  type=str,
710
737
  callback=validate_option,
711
- prompt="Enter Resource Group Name (Creates a new Resource Group if not found):",
712
738
  help="The Resource Group Name (Creates a new Resource Group if not found).")
713
739
  @click.option('--iothub-sku',
714
740
  envvar=envvars.get_envvar_key_if_val("IOTHUB_SKU"),
715
741
  required=True,
716
- default=lambda: header_and_default('IOTHUB SKU', 'F1'),
717
- type=click.Choice(['F1', 'S1', 'S2', 'S3']),
742
+ default=lambda: header_and_default('IOTHUB SKU', 'Enter IoT Hub SKU', 'F1', ['F1', 'S1', 'S2', 'S3']),
743
+ type=str,
718
744
  callback=validate_option,
719
- prompt="Enter IoT Hub SKU (F1|S1|S2|S3):",
720
745
  help="The IoT Hub SKU.")
721
746
  @click.option('--iothub-name',
722
747
  envvar=envvars.get_envvar_key_if_val("IOTHUB_NAME"),
723
748
  required=True,
724
- default=lambda: list_iot_hubs_and_set_default(),
749
+ default=list_iot_hubs_and_set_default,
725
750
  type=str,
726
751
  callback=validate_option,
727
- prompt='Enter the IoT Hub Name (Creates a new IoT Hub if not found):',
728
752
  help='The IoT Hub Name (Creates a new IoT Hub if not found).')
729
753
  @click.option('--edge-device-id',
730
754
  envvar=envvars.get_envvar_key_if_val("EDGE_DEVICE_ID"),
731
755
  required=True,
732
- default=lambda: list_edge_devices_and_set_default(),
756
+ default=list_edge_devices_and_set_default,
733
757
  type=str,
734
758
  callback=validate_option,
735
- prompt='Enter the IoT Edge Device Id (Creates a new Edge Device if not found):',
736
759
  help='The IoT Edge Device Id (Creates a new Edge Device if not found).')
737
760
  @click.option('--update-dotenv',
738
761
  "-u",
@@ -743,6 +766,14 @@ def header_and_default(header, default, default2=None):
743
766
  is_flag=True,
744
767
  prompt='Update the .env file with connection strings?',
745
768
  help='If True, the current .env will be updated with the IoT Hub and Device connection strings.')
769
+ @click.option('--create-edge-vm',
770
+ envvar=envvars.get_envvar_key_if_val("CREATE_EDGE_VM"),
771
+ required=True,
772
+ default=False,
773
+ show_default=True,
774
+ is_flag=True,
775
+ prompt='Create a new Virtual Machine with IoTEdge?',
776
+ help='If true, creates a new Virtual Machine with IoTEdge.')
746
777
  @with_telemetry
747
778
  def setup_iothub(credentials,
748
779
  service_principal,
@@ -752,13 +783,17 @@ def setup_iothub(credentials,
752
783
  iothub_sku,
753
784
  iothub_name,
754
785
  edge_device_id,
755
- update_dotenv):
786
+ update_dotenv,
787
+ create_edge_vm):
756
788
  if update_dotenv:
757
789
  if envvars.backup_dotenv():
758
790
  envvars.save_envvar("IOTHUB_CONNECTION_STRING", envvars.IOTHUB_CONNECTION_STRING)
759
791
  envvars.save_envvar("DEVICE_CONNECTION_STRING", envvars.DEVICE_CONNECTION_STRING)
760
792
  output.info("Updated current .env file")
761
793
 
794
+ if create_edge_vm:
795
+ handle_vm_create()
796
+
762
797
 
763
798
  @docker.command(context_settings=CONTEXT_SETTINGS,
764
799
  help="Pull Edge runtime images from Microsoft Container Registry and push to your specified container registry. "
@@ -835,7 +870,7 @@ def log(show, save):
835
870
  dock.handle_logs_cmd(show, save)
836
871
 
837
872
 
838
- main.add_command_with_deprecation(log, deprecated=True)
873
+ main.add_command_with_deprecation(log, deprecated=True, alt="docker log")
839
874
 
840
875
  if __name__ == "__main__":
841
876
  main()
@@ -119,6 +119,7 @@ class EnvVars:
119
119
  self.IOTHUB_NAME = self.get_envvar("IOTHUB_NAME")
120
120
  self.IOTHUB_SKU = self.get_envvar("IOTHUB_SKU")
121
121
  self.EDGE_DEVICE_ID = self.get_envvar("EDGE_DEVICE_ID")
122
+ self.CREATE_EDGE_VM = self.get_envvar("CREATE_EDGE_VM")
122
123
  self.CREDENTIALS = self.get_envvar("CREDENTIALS")
123
124
  self.UPDATE_DOTENV = self.get_envvar("UPDATE_DOTENV")
124
125
 
@@ -55,3 +55,7 @@ class Output:
55
55
 
56
56
  def confirm(self, text, default=False, abort=True):
57
57
  return click.confirm(text, default=default, abort=abort)
58
+
59
+ def prompt_question(self, text, default=""):
60
+ self.line()
61
+ return click.prompt(text, default=default)
s/docs/command-list.md CHANGED
@@ -335,6 +335,7 @@ Options:
335
335
  --iothub-name TEXT The IoT Hub Name (Creates a new IoT Hub if not found). [required]
336
336
  --edge-device-id TEXT The IoT Edge Device Id (Creates a new Edge Device if not found). [required]
337
337
  -u, --update-dotenv If True, the current .env will be updated with the IoT Hub and Device connection strings. [default: False; required]
338
+ --create-edge-vm Create an Edge-enabled Virtual Machine by using <https://aka.ms/iotedge-vm-deploy>. [default: False; required]
338
339
  -h, --help Show this message and exit.
339
340
  ```
340
341
 
@@ -76,4 +76,4 @@ If you are using a separate Edge device, like a Raspberry Pi, you do not need to
76
76
  1. Install **[JDK](https://www.oracle.com/technetwork/java/javase/downloads/index.html)**
77
77
  2. Install **[Maven](https://maven.apache.org/)**
78
78
 
79
- 9. Follow the [Usage Wiki](usage) to learn the usage of IoT Edge Dev Tool
79
+ 9. Follow the [Usage Wiki](../usage.md) to learn the usage of IoT Edge Dev Tool
s/docs/quickstart.md CHANGED
@@ -22,9 +22,9 @@ At the end of this quickstart, you should have the following:
22
22
 
23
23
  There are three options to setup your environment:
24
24
 
25
- - Setup the development environment manually. Please follow the [Manual Development Machine Setup Wiki](environment-setup/manual-dev-machine-setup).
25
+ - Setup the development environment manually. Please follow the [Manual Development Machine Setup Wiki](environment-setup/manual-dev-machine-setup.md).
26
26
  - Starting the devcontainer in VS Code (see [Developing inside a Container](https://code.visualstudio.com/docs/remote/containers) for steps on how to do so).
27
- - Starting the devcontainer with Docker. Please follow the [Run the IoT Edge Dev Container with Docker docs](environment-setup/run-devcontainer-docker).
27
+ - Starting the devcontainer with Docker. Please follow the [Run the IoT Edge Dev Container with Docker docs](environment-setup/run-devcontainer-docker.md).
28
28
 
29
29
  1. Initialize IoT Edge solution and setup Azure resources.
30
30
 
s/iotedgedev/__init__.py CHANGED
@@ -2,5 +2,5 @@
2
2
 
3
3
  __author__ = 'Microsoft Corporation'
4
4
  __email__ = 'opencode@microsoft.com'
5
- __version__ = '3.3.5'
5
+ __version__ = '3.3.7'
6
6
  __AIkey__ = 'fed7fc65-5b4a-4e66-9d46-c5f016d4e2b4'
s/iotedgedev/azurecli.py CHANGED
@@ -6,6 +6,7 @@ import subprocess
6
6
  import sys
7
7
  from io import StringIO
8
8
  from threading import Thread, Timer
9
+ from pathlib import Path
9
10
 
10
11
  from azure.cli.core import get_default_cli
11
12
  from fstrings import f
@@ -34,14 +35,17 @@ class AzureCli:
34
35
  def is_posix(self):
35
36
  return self.envvars.is_posix()
36
37
 
37
- def prepare_az_cli_args(self, args, suppress_output=False):
38
+ def prepare_az_cli_args(self, args, suppress_output=False, ensure_json_output=False):
38
39
  if suppress_output:
39
40
  args.extend(["--query", "\"[?n]|[0]\""])
40
41
 
42
+ if ensure_json_output:
43
+ args.extend(["--output", "json"])
44
+
41
45
  az_args = ["az"] + args
42
46
  return az_args
43
47
 
44
- def invoke_az_cli_outproc(self, args, error_message=None, stdout_io=None, stderr_io=None, suppress_output=False, timeout=None):
48
+ def invoke_az_cli_outproc(self, args, error_message=None, stdout_io=None, stderr_io=None, suppress_output=False, timeout=None, ensure_json_output=False):
45
49
  try:
46
50
  if timeout:
47
51
  timeout = int(timeout)
@@ -53,19 +57,19 @@ class AzureCli:
53
57
 
54
58
  # Consider using functools
55
59
  if monitor_events:
56
- process = subprocess.Popen(self.prepare_az_cli_args(args, suppress_output),
60
+ process = subprocess.Popen(self.prepare_az_cli_args(args, suppress_output, ensure_json_output),
57
61
  shell=not self.is_posix(),
58
62
  stdout=subprocess.PIPE,
59
63
  stderr=subprocess.PIPE,
60
64
  preexec_fn=os.setsid if self.is_posix() else None,
61
65
  creationflags=subprocess.CREATE_NEW_PROCESS_GROUP if not self.is_posix() else 0)
62
66
  elif stdout_io or stderr_io:
63
- process = subprocess.Popen(self.prepare_az_cli_args(args, suppress_output),
67
+ process = subprocess.Popen(self.prepare_az_cli_args(args, suppress_output, ensure_json_output),
64
68
  stdout=subprocess.PIPE,
65
69
  stderr=subprocess.PIPE,
66
70
  shell=not self.is_posix())
67
71
  else:
68
- process = subprocess.Popen(self.prepare_az_cli_args(args, suppress_output),
72
+ process = subprocess.Popen(self.prepare_az_cli_args(args, suppress_output, ensure_json_output),
69
73
  shell=not self.is_posix())
70
74
 
71
75
  self.process = process
@@ -226,8 +230,9 @@ class AzureCli:
226
230
  self.output.status(f("Retrieving Azure CLI credentials from cache..."))
227
231
 
228
232
  with output_io_cls() as io:
229
- result = self.invoke_az_cli_outproc(
230
- ["account", "show"], stdout_io=io)
233
+ result = self.invoke_az_cli_outproc(["account", "show"],
234
+ stdout_io=io,
235
+ ensure_json_output=True)
231
236
 
232
237
  if result:
233
238
  try:
@@ -269,7 +274,9 @@ class AzureCli:
269
274
 
270
275
  with output_io_cls() as io:
271
276
  result = self.invoke_az_cli_outproc(["account", "show"],
272
- "Error while trying to get the default Azure subscription id.", io)
277
+ "Error while trying to get the default Azure subscription id.",
278
+ io,
279
+ ensure_json_output=True)
273
280
  if result:
274
281
  out_string = io.getvalue()
275
282
  data = json.loads(out_string)
@@ -280,12 +287,13 @@ class AzureCli:
280
287
  with output_io_cls() as io:
281
288
  query = get_query_argument_for_id_and_name(token)
282
289
  result = self.invoke_az_cli_outproc(["account", "list", "--query", query],
283
- "Could not find a subscription for which the id starts with or name contains '{0}'".format(token), io)
290
+ "Could not find a subscription for which the id starts with or name contains '{0}'".format(token),
291
+ io,
292
+ ensure_json_output=True)
284
293
 
285
294
  if result:
286
295
  out_string = io.getvalue()
287
296
  if out_string:
288
-
289
297
  data = json.loads(out_string)
290
298
  if len(data) == 1:
291
299
  return data[0]["id"]
@@ -417,7 +425,10 @@ class AzureCli:
417
425
  def get_free_iothub(self):
418
426
  with output_io_cls() as io:
419
427
 
420
- result = self.invoke_az_cli_outproc(["iot", "hub", "list"], f("Could not list IoT Hubs in subscription."), stdout_io=io)
428
+ result = self.invoke_az_cli_outproc(["iot", "hub", "list"],
429
+ f("Could not list IoT Hubs in subscription."),
430
+ stdout_io=io,
431
+ ensure_json_output=True)
421
432
  if result:
422
433
  out_string = io.getvalue()
423
434
  data = json.loads(out_string)
@@ -430,7 +441,10 @@ class AzureCli:
430
441
 
431
442
  with output_io_cls() as io:
432
443
  result = self.invoke_az_cli_outproc(
433
- ["iot", "hub", "list", "--resource-group", resource_group, "--query", "[0]"], f("Could not get first IoT Hub."), io)
444
+ ["iot", "hub", "list", "--resource-group", resource_group, "--query", "[0]"],
445
+ f("Could not get first IoT Hub."),
446
+ io,
447
+ ensure_json_output=True)
434
448
 
435
449
  if result:
436
450
  out_string = io.getvalue()
@@ -491,9 +505,10 @@ class AzureCli:
491
505
  f("Retrieving '{value}' connection string..."))
492
506
 
493
507
  with output_io_cls() as io:
494
- result = self.invoke_az_cli_outproc(["iot", "hub", "connection-string", "show", "--hub-name", value,
495
- "--resource-group", resource_group],
496
- f("Could not create the IoT Hub {value} in {resource_group}."), stdout_io=io)
508
+ result = self.invoke_az_cli_outproc(["iot", "hub", "connection-string", "show", "--hub-name", value, "--resource-group", resource_group],
509
+ f("Could not create the IoT Hub {value} in {resource_group}."),
510
+ stdout_io=io,
511
+ ensure_json_output=True)
497
512
  if result:
498
513
  out_string = io.getvalue()
499
514
  data = json.loads(out_string)
@@ -538,9 +553,10 @@ class AzureCli:
538
553
  f("Retrieving '{value}' connection string..."))
539
554
 
540
555
  with output_io_cls() as io:
541
- result = self.invoke_az_cli_outproc(["iot", "hub", "device-identity", "connection-string", "show", "--device-id", value, "--hub-name", iothub,
542
- "--resource-group", resource_group],
543
- f("Could not locate the {value} device in {iothub} IoT Hub in {resource_group}."), stdout_io=io)
556
+ result = self.invoke_az_cli_outproc(["iot", "hub", "device-identity", "connection-string", "show", "--device-id", value, "--hub-name", iothub, "--resource-group", resource_group],
557
+ f("Could not locate the {value} device in {iothub} IoT Hub in {resource_group}."),
558
+ stdout_io=io,
559
+ ensure_json_output=True)
544
560
  if result:
545
561
  out_string = io.getvalue()
546
562
  data = json.loads(out_string)
@@ -550,3 +566,35 @@ class AzureCli:
550
566
  return data["connectionString"]
551
567
 
552
568
  return ''
569
+
570
+ def get_ssh_key(self, ssh_key_file):
571
+ file_path = Path.home().joinpath(".ssh", ssh_key_file + ".pub")
572
+ if not os.path.exists(file_path):
573
+ self.output.status(f("No SSH key found at ~/.ssh/{ssh_key_file}.pub. Generating a new one..."))
574
+ os.system(f("ssh-keygen -t rsa -b 4096 -C \"iotedgedev-autogenerated\" -f ~/.ssh/{ssh_key_file} -q -N \"\""))
575
+
576
+ with open(file_path, 'r') as file:
577
+ ssh_key = file.read()
578
+ return ssh_key
579
+
580
+ def create_edge_vm(self, ssh_key_file, edge_device_id, iothub, resource_group):
581
+ self.output.status(f("\nCreating a new Virtual Machine in '{resource_group}' with ssh key file '{ssh_key_file}'..."))
582
+
583
+ connectionString = self.get_device_connection_string(edge_device_id, iothub, resource_group)
584
+ # Use IoTHub name for deployment - in this case it will create a new
585
+ # template if a new IoT Hub is created.
586
+ deploymentTemplateName = "edgeVM-" + iothub
587
+
588
+ ssh_key = self.get_ssh_key(ssh_key_file)
589
+
590
+ self.output.status(f("Creating Virtual Machine in '{resource_group}' with an incremental deployment template '{deploymentTemplateName}', this can take some time..."))
591
+ return self.invoke_az_cli_outproc(["deployment", "group", "create", "--name", deploymentTemplateName,
592
+ "--resource-group", resource_group,
593
+ "--template-uri", "https://raw.githubusercontent.com/Azure/iotedge-vm-deploy/master/edgeDeploy.json",
594
+ "--parameters", "dnsLabelPrefix=iotedgedev-vm",
595
+ "--parameters", "adminUsername=azureuser",
596
+ "--parameters", "authenticationType=sshPublicKey",
597
+ "--parameters", "adminPasswordOrKey=" + ssh_key,
598
+ "--parameters", "deviceConnectionString=" + connectionString,
599
+ "--output", "table"],
600
+ f("Failed to create the VM with deployment template '{deploymentTemplateName}'."))