Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add narg kvp to configuration/deployment create and create inline json wiki page #610

Merged
merged 10 commits into from
Jan 18, 2023
3 changes: 3 additions & 0 deletions HISTORY.rst
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,9 @@ unreleased
* Modification of how route testing works for testing all route sources. If `az iot hub message-route test` is called
without specifying a route name or type, all types will be tested rather than only DeviceMessage routes.

* Addition of new parameters is introduced in `az iot hub configuration create` and
`az iot edge deployment create` to let user insert labels and metrics in key-value pair style

Elsie4ever marked this conversation as resolved.
Show resolved Hide resolved

**Digital Twins updates**

Expand Down
13 changes: 13 additions & 0 deletions azext_iot/_help.py
Original file line number Diff line number Diff line change
Expand Up @@ -764,6 +764,11 @@
--content '{\\"moduleContent\\": {\\"properties.desired.chillerWaterSettings\\": {\\"temperature\\": 38, \\"pressure\\": 78}}}'
--target-condition "from devices.modules where tags.building=9" --priority 1
--metrics '{\\"metrics\\": {\\"queries\\": {\\"mymetric\\":\\"select moduleId from devices.modules where tags.location=''US''\\"}}}'
- name: Create a device configuration with an alternative input style of labels and metrics (powershell syntax example)
text: >
az iot hub configuration create -c {config_name} -n {iothub_name} --content device_content.json
--target-condition "from devices.modules where tags.building=9" --custom-labels key0="value0", key1="value1" --priority 10
--custom-metric-queries mymetric1="select deviceId from devices where tags.location='US'" mymetric2="select *"
"""

helps[
Expand Down Expand Up @@ -1096,6 +1101,14 @@
--target-condition "tags.building=9 and tags.environment='test'"
--metrics metrics_content.json
--layered
- name: Create a layered deployment with an alternative input style of labels and metrics (powershell syntax example)
Elsie4ever marked this conversation as resolved.
Show resolved Hide resolved
text: >
az iot edge deployment create -d {deployment_name} -n {iothub_name}
--content layered_modules_content.json
--target-condition "tags.building=9 and tags.environment='test'"
--custom-labels key0="value0", key1="value1"
Elsie4ever marked this conversation as resolved.
Show resolved Hide resolved
--custom-metric-queries mymetric1="select deviceId from devices where tags.location='US'" mymetric2="select *"
--layered
"""

helps[
Expand Down
24 changes: 24 additions & 0 deletions azext_iot/_params.py
Original file line number Diff line number Diff line change
Expand Up @@ -819,6 +819,18 @@ def load_arguments(self, _):
type=int,
help="Maximum number of configurations to return. By default all configurations are returned.",
)
context.argument(
"custom_metric_queries",
nargs="+",
options_list=["--custom-metric-queries"],
Elsie4ever marked this conversation as resolved.
Show resolved Hide resolved
help="An altervative way of input style(narg key-value pair) to --metrics.",
Elsie4ever marked this conversation as resolved.
Show resolved Hide resolved
)
context.argument(
"custom_labels",
nargs="+",
options_list=["--custom-labels"],
help="An altervative way of input style(narg key-value pair) to --labels.",
Elsie4ever marked this conversation as resolved.
Show resolved Hide resolved
)

with self.argument_context("iot edge") as context:
context.argument(
Expand Down Expand Up @@ -879,6 +891,18 @@ def load_arguments(self, _):
options_list=["--auth-type"],
arg_type=hub_auth_type_dataplane_param_type,
)
context.argument(
"custom_metric_queries",
nargs="+",
options_list=["--custom-metric-queries"],
help="An altervative way of input style(narg key-value pair) to --metrics.",
)
context.argument(
"custom_labels",
nargs="+",
options_list=["--custom-labels"],
help="An altervative way of input style(narg key-value pair) to --labels.",
)

with self.argument_context("iot dps") as context:
context.argument(
Expand Down
9 changes: 2 additions & 7 deletions azext_iot/deviceupdate/_help.py
Original file line number Diff line number Diff line change
Expand Up @@ -842,13 +842,8 @@ def load_deviceupdate_help():
`--related-file`. Review examples and parameter descriptions for details on how
to fully utilize the operation.

- For bash inline json format use '{"key":"value"}' and \\ (backslash) for command continuation.
- For powershell inline json format use '{\\"key\\":\\"value\\"}' and ` (tilde) for command continuation.
- For cmd inline json format use \"{\\"key\\":\\"value\\"}\" and ^ (caret) for command continuation.
- For file based json input use "@/path/to/file". File based input avoids shell quotation issues.

For a detailed explanation of shell quoting rules please goto
https://learn.microsoft.com/en-us/cli/azure/use-cli-effectively
Read more about using quotation marks and escapes characters in different shells here
Elsie4ever marked this conversation as resolved.
Show resolved Hide resolved
https://github.com/Azure/azure-iot-cli-extension/wiki/Inline-JSON-help
Elsie4ever marked this conversation as resolved.
Show resolved Hide resolved

examples:
- name: Initialize a minimum content import manifest. Inline json optimized for `bash`.
Expand Down
15 changes: 15 additions & 0 deletions azext_iot/operations/hub.py
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@
process_json_arg,
generate_key,
generate_storage_account_sas_token,
validate_key_value_pairs
)
from azext_iot._factory import SdkResolver, CloudError
from azext_iot.operations.generic import _execute_query, _process_top
Expand Down Expand Up @@ -1188,6 +1189,8 @@ def iot_edge_deployment_create(
cmd,
config_id,
content,
custom_labels=None,
custom_metric_queries=None,
hub_name=None,
target_condition="",
priority=0,
Expand All @@ -1205,6 +1208,8 @@ def iot_edge_deployment_create(
cmd=cmd,
config_id=config_id,
content=content,
custom_labels=custom_labels,
custom_metric_queries=custom_metric_queries,
hub_name=hub_name,
target_condition=target_condition,
priority=priority,
Expand All @@ -1221,6 +1226,8 @@ def iot_hub_configuration_create(
cmd,
config_id,
content,
custom_labels=None,
custom_metric_queries=None,
hub_name=None,
target_condition="",
priority=0,
Expand All @@ -1234,6 +1241,8 @@ def iot_hub_configuration_create(
cmd=cmd,
config_id=config_id,
content=content,
custom_labels=custom_labels,
custom_metric_queries=custom_metric_queries,
hub_name=hub_name,
target_condition=target_condition,
priority=priority,
Expand All @@ -1251,6 +1260,8 @@ def _iot_hub_configuration_create(
config_id,
content,
config_type,
custom_labels=None,
custom_metric_queries=None,
hub_name=None,
target_condition="",
priority=0,
Expand Down Expand Up @@ -1302,9 +1313,13 @@ def _iot_hub_configuration_create(
"metrics json must include the '{}' property".format(metrics_key)
)
metrics = metrics[metrics_key]
elif custom_metric_queries:
metrics = validate_key_value_pairs(";".join(custom_metric_queries))
Elsie4ever marked this conversation as resolved.
Show resolved Hide resolved

if labels:
labels = process_json_arg(labels, argument_name="labels")
elif custom_labels:
labels = validate_key_value_pairs(";".join(custom_labels))

config_content = ConfigurationContent(**processed_content)

Expand Down
84 changes: 81 additions & 3 deletions azext_iot/tests/iothub/configurations/test_iot_config_int.py
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,7 @@ def test_edge_set_modules(self):

def test_edge_deployments(self):
for auth_phase in DATAPLANE_AUTH_TYPES:
config_count = 5
config_count = 6
config_ids = self.generate_config_names(config_count)

self.kwargs["generic_metrics"] = read_file_content(generic_metrics_path)
Expand All @@ -96,6 +96,9 @@ def test_edge_deployments(self):
edge_content_malformed_path
)
self.kwargs["labels"] = '{"key0": "value0"}'
self.kwargs["custom_labels"] = "key0=value0"
digimaun marked this conversation as resolved.
Show resolved Hide resolved
self.kwargs["custom_metric_queries"] = "mymetric=SELECT deviceId FROM devices {}".format(
digimaun marked this conversation as resolved.
Show resolved Hide resolved
"WHERE properties.reported.lastDesiredStatus.code = 200")

priority = random.randint(1, 10)
condition = "tags.building=9 and tags.environment='test'"
Expand Down Expand Up @@ -136,7 +139,7 @@ def test_edge_deployments(self):
self.cmd(
self.set_cmd_auth_type(
"iot edge deployment create -d {} --pri {} --tc \"{}\" --lab '{}' -k '{}' --metrics '{}' -n {} -g {}".format(
config_ids[1].upper(),
config_ids[5],
priority,
condition,
"{labels}",
Expand Down Expand Up @@ -165,6 +168,41 @@ def test_edge_deployments(self):
],
)

# Metrics + labels using narg kvp parameters. Configurations must be lowercase and will be lower()'ed.
# Note: $schema is included as a nested property in the sample content.
self.cmd(
self.set_cmd_auth_type(
"""iot edge deployment create -d {} --pri {} --tc \"{}\" --custom-labels '{}' -k '{}'
--custom-metric-queries '{}' -n {} -g {}""".format(
config_ids[1].upper(),
priority,
condition,
"{custom_labels}",
edge_content_path,
"{custom_metric_queries}",
self.entity_name,
self.entity_rg
),
auth_type=auth_phase
),
checks=[
self.check("id", config_ids[1].lower()),
self.check("priority", priority),
self.check("targetCondition", condition),
self.check("labels", json.loads(self.kwargs["labels"])),
Elsie4ever marked this conversation as resolved.
Show resolved Hide resolved
self.check(
"content.modulesContent",
json.loads(self.kwargs["edge_content"])["content"][
"modulesContent"
],
),
self.check(
"metrics.queries",
json.loads(self.kwargs["edge_content"])["metrics"]["queries"],
Elsie4ever marked this conversation as resolved.
Show resolved Hide resolved
),
],
)

# Layered deployment with content + metrics from file.
# No labels, target-condition or priority
self.cmd(
Expand Down Expand Up @@ -433,7 +471,7 @@ def test_edge_deployments(self):
self.tearDown()

def test_device_configurations(self):
config_count = 3
config_count = 4
config_ids = self.generate_config_names(config_count)
edge_config_ids = self.generate_config_names(1, True)

Expand All @@ -442,6 +480,9 @@ def test_device_configurations(self):
self.kwargs["adm_content_module"] = read_file_content(adm_content_module_path)
self.kwargs["edge_content"] = read_file_content(edge_content_path)
self.kwargs["labels"] = '{"key0": "value0"}'
self.kwargs["custom_labels"] = "key0=value0"
self.kwargs["custom_metric_queries"] = "mymetric=SELECT deviceId FROM devices {}".format(
"WHERE properties.reported.lastDesiredStatus.code = 200")

priority = random.randint(1, 10)
condition = "tags.building=9 and tags.environment='test'"
Expand Down Expand Up @@ -514,6 +555,43 @@ def test_device_configurations(self):
],
)

# Metrics + labels using narg kvp parameters.
# Configurations must be lowercase and will be lower()'ed.
# Note: $schema is included as a nested property in the sample content.
self.cmd(
self.set_cmd_auth_type(
"""iot hub configuration create -c {} --pri {} --tc \"{}\" --custom-labels '{}'
-k '{}' --custom-metric-queries '{}' -n {} -g {}"""
.format(
config_ids[3].upper(),
priority,
module_condition,
"{custom_labels}",
adm_content_module_path,
"{custom_metric_queries}",
self.entity_name,
self.entity_rg
),
auth_type=auth_phase,
),
checks=[
self.check("id", config_ids[3].lower()),
self.check("priority", priority),
self.check("targetCondition", module_condition),
self.check("labels", json.loads(self.kwargs["labels"])),
self.check(
"content.moduleContent",
json.loads(self.kwargs["adm_content_module"])["content"][
"moduleContent"
],
),
self.check(
"metrics.queries",
json.loads(self.kwargs["adm_content_module"])["metrics"]["queries"],
),
],
)

# Device content + metrics from file.
# Configurations must be lowercase and will be lower()'ed.
# No labels, target-condition or priority
Expand Down
Loading