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.
- s/.git/FETCH_HEAD +3 -1
- s/.git/HEAD +1 -1
- s/.git/index +0 -0
- s/.git/logs/HEAD +1 -1
- s/.git/logs/refs/remotes/origin/EO +1 -1
- s/.git/logs/refs/remotes/origin/avagraw/security_fix +1 -1
- s/.git/logs/refs/remotes/origin/cherry-pick-rename +1 -1
- s/.git/logs/refs/remotes/origin/chyuan/fix-validation-not-fail-test +1 -1
- s/.git/logs/refs/remotes/origin/chyuan/update-linux-agent +1 -1
- s/.git/logs/refs/remotes/origin/dependabot/pip/cookiecutter-2.1.1 +1 -1
- s/.git/logs/refs/remotes/origin/dependabot/pip/pyyaml-5.4 +1 -1
- s/.git/logs/refs/remotes/origin/main +1 -1
- s/.git/logs/refs/remotes/origin/ray-deps +1 -1
- s/.git/logs/refs/remotes/origin/ray-dev-setup +1 -1
- s/.git/logs/refs/remotes/origin/ray-tox +1 -1
- s/.git/logs/refs/remotes/origin/release-pipeline-automation +1 -1
- s/.git/logs/refs/remotes/origin/revert-594-user/vikas_bug-pyopenssl +1 -0
- s/.git/logs/refs/remotes/origin/spmcdono/fix-pipeline-test-install +1 -1
- s/.git/logs/refs/remotes/origin/spmcdono/layered-deployments +1 -1
- s/.git/logs/refs/remotes/origin/spmcdono/pypi-package-test-pipeline +1 -1
- s/.git/logs/refs/remotes/origin/spmcdono/remove-py2 +1 -1
- s/.git/logs/refs/remotes/origin/spmcdono/remove-py2-dev +1 -1
- s/.git/logs/refs/remotes/origin/spmcdono/test-native-runtime-install +1 -1
- s/.git/logs/refs/remotes/origin/spmcdono/update-pipeline +1 -1
- s/.git/logs/refs/remotes/origin/user/marine/EliiseS/devcontainer +1 -1
- s/.git/logs/refs/remotes/origin/user/marine/fixsimver +1 -1
- s/.git/logs/refs/remotes/origin/user/marine/layereddeploymentsbuildtest +1 -1
- s/.git/logs/refs/remotes/origin/user/marine/master-to-main +1 -1
- s/.git/logs/refs/remotes/origin/user/marine/pipeline +1 -1
- s/.git/logs/refs/remotes/origin/user/marine/updatesimver +1 -0
- s/.git/logs/refs/remotes/origin/user/vikas +1 -1
- s/.git/logs/refs/remotes/origin/user/vikas-bud-fix +1 -0
- s/.git/logs/refs/remotes/origin/user/vikas0212/verify-tests +1 -1
- s/.git/logs/refs/remotes/origin/user/vikas2 +1 -1
- s/.git/logs/refs/remotes/origin/user/vikas_bug-pyopenssl +1 -0
- s/.git/logs/refs/remotes/origin/user/vikas_pypi +1 -1
- s/.git/logs/refs/remotes/origin/user/vikas_pypi-1 +1 -1
- s/.git/logs/refs/remotes/origin/user/vikas_supplychain-issue1 +1 -1
- s/.git/logs/refs/remotes/origin/user/vikas_testing +1 -1
- s/.git/logs/refs/remotes/origin/user/vikas_trigger +1 -1
- s/.git/logs/refs/remotes/origin/user/vikas_trigger_new +1 -1
- s/.git/logs/refs/remotes/origin/user/xinzed/dps_conn_str +1 -1
- s/.git/logs/refs/remotes/origin/user/xinzed/update-python-docker +1 -1
- s/.git/logs/refs/remotes/origin/vikas/azure +1 -1
- s/.git/logs/refs/remotes/origin/vikas/dependebot +1 -1
- s/.git/logs/refs/remotes/origin/vikas-triggers +1 -1
- s/.git/logs/refs/remotes/origin/xinzed/3_1_0rc1 +1 -1
- s/.git/logs/refs/remotes/origin/xinzed/update-runtime +1 -1
- s/.git/objects/pack/{pack-7a60b35c219082b6cfceccd45f3712d11b9c0e3b.idx → pack-a04ba9522e6fa097b45076bd52c4ffab33f906ac.idx} +0 -0
- s/.git/objects/pack/{pack-7a60b35c219082b6cfceccd45f3712d11b9c0e3b.pack → pack-a04ba9522e6fa097b45076bd52c4ffab33f906ac.pack} +0 -0
- s/.git/refs/remotes/origin/dependabot/pip/cookiecutter-2.1.1 +1 -1
- s/.git/refs/remotes/origin/main +1 -1
- s/.git/refs/remotes/origin/revert-594-user/vikas_bug-pyopenssl +1 -0
- s/.git/refs/remotes/origin/user/marine/updatesimver +1 -0
- s/.git/refs/remotes/origin/user/vikas-bud-fix +1 -0
- s/.git/refs/remotes/origin/user/vikas_bug-pyopenssl +1 -0
- s/.git/refs/tags/v3.3.6 +1 -0
- s/.git/refs/tags/v3.3.7 +1 -0
- s/CHANGELOG.md +9 -0
- s/build/lib/iotedgedev/__init__.py +1 -1
- s/build/lib/iotedgedev/azurecli.py +66 -18
- s/build/lib/iotedgedev/cli.py +75 -40
- s/build/lib/iotedgedev/envvars.py +1 -0
- s/build/lib/iotedgedev/output.py +4 -0
- s/dist/{iotedgedev-3.3.5-py3-none-any.whl → iotedgedev-3.3.7-py3-none-any.whl} +0 -0
- s/docs/command-list.md +1 -0
- s/docs/environment-setup/manual-dev-machine-setup.md +1 -1
- s/docs/quickstart.md +2 -2
- s/iotedgedev/__init__.py +1 -1
- s/iotedgedev/azurecli.py +66 -18
- s/iotedgedev/cli.py +75 -40
- s/iotedgedev/envvars.py +1 -0
- s/iotedgedev/output.py +4 -0
- s/iotedgedev.egg-info/PKG-INFO +1 -1
- s/iotedgedev.egg-info/requires.txt +1 -1
- s/requirements.txt +1 -1
- s/setup.cfg +1 -1
- s/setup.py +2 -2
- s/tests/__init__.py +1 -1
s/build/lib/iotedgedev/cli.py
CHANGED
|
@@ -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
|
-
|
|
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
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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,
|
|
676
|
+
def header_and_default(header, prompt, default, options=None):
|
|
664
677
|
output.header(header)
|
|
665
|
-
|
|
666
|
-
|
|
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=
|
|
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
|
|
700
|
-
|
|
701
|
-
|
|
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=
|
|
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=
|
|
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=
|
|
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=
|
|
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
|
|
s/build/lib/iotedgedev/output.py
CHANGED
|
Binary file
|
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
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
|
-
|
|
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.",
|
|
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),
|
|
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"],
|
|
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]"],
|
|
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
|
-
|
|
496
|
-
|
|
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
|
-
|
|
543
|
-
|
|
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}'."))
|