esp-rainmaker-cli 1.10.0__tar.gz → 1.11.1__tar.gz
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.
- {esp_rainmaker_cli-1.10.0 → esp_rainmaker_cli-1.11.1}/PKG-INFO +8 -1
- {esp_rainmaker_cli-1.10.0 → esp_rainmaker_cli-1.11.1}/esp_rainmaker_cli.egg-info/PKG-INFO +8 -1
- {esp_rainmaker_cli-1.10.0 → esp_rainmaker_cli-1.11.1}/rainmaker/rainmaker.py +102 -0
- {esp_rainmaker_cli-1.10.0 → esp_rainmaker_cli-1.11.1}/rainmaker/version.py +1 -1
- {esp_rainmaker_cli-1.10.0 → esp_rainmaker_cli-1.11.1}/rmaker_cmd/node.py +201 -1
- {esp_rainmaker_cli-1.10.0 → esp_rainmaker_cli-1.11.1}/rmaker_cmd/provision.py +20 -3
- {esp_rainmaker_cli-1.10.0 → esp_rainmaker_cli-1.11.1}/rmaker_lib/node.py +178 -4
- {esp_rainmaker_cli-1.10.0 → esp_rainmaker_cli-1.11.1}/rmaker_tools/rmaker_claim/claim.py +15 -3
- {esp_rainmaker_cli-1.10.0 → esp_rainmaker_cli-1.11.1}/rmaker_tools/rmaker_prov/challenge_response.py +18 -3
- {esp_rainmaker_cli-1.10.0 → esp_rainmaker_cli-1.11.1}/rmaker_tools/rmaker_prov/esp_rainmaker_prov.py +4 -2
- {esp_rainmaker_cli-1.10.0 → esp_rainmaker_cli-1.11.1}/rmaker_tools/rmaker_prov/on_network_chal_resp.py +19 -4
- {esp_rainmaker_cli-1.10.0 → esp_rainmaker_cli-1.11.1}/LICENSE +0 -0
- {esp_rainmaker_cli-1.10.0 → esp_rainmaker_cli-1.11.1}/README.md +0 -0
- {esp_rainmaker_cli-1.10.0 → esp_rainmaker_cli-1.11.1}/esp_rainmaker_cli.egg-info/SOURCES.txt +0 -0
- {esp_rainmaker_cli-1.10.0 → esp_rainmaker_cli-1.11.1}/esp_rainmaker_cli.egg-info/dependency_links.txt +0 -0
- {esp_rainmaker_cli-1.10.0 → esp_rainmaker_cli-1.11.1}/esp_rainmaker_cli.egg-info/entry_points.txt +0 -0
- {esp_rainmaker_cli-1.10.0 → esp_rainmaker_cli-1.11.1}/esp_rainmaker_cli.egg-info/requires.txt +0 -0
- {esp_rainmaker_cli-1.10.0 → esp_rainmaker_cli-1.11.1}/esp_rainmaker_cli.egg-info/top_level.txt +0 -0
- {esp_rainmaker_cli-1.10.0 → esp_rainmaker_cli-1.11.1}/rainmaker/__init__.py +0 -0
- {esp_rainmaker_cli-1.10.0 → esp_rainmaker_cli-1.11.1}/rmaker_cmd/__init__.py +0 -0
- {esp_rainmaker_cli-1.10.0 → esp_rainmaker_cli-1.11.1}/rmaker_cmd/browserlogin.py +0 -0
- {esp_rainmaker_cli-1.10.0 → esp_rainmaker_cli-1.11.1}/rmaker_cmd/cmd_response.py +0 -0
- {esp_rainmaker_cli-1.10.0 → esp_rainmaker_cli-1.11.1}/rmaker_cmd/group.py +0 -0
- {esp_rainmaker_cli-1.10.0 → esp_rainmaker_cli-1.11.1}/rmaker_cmd/html/welcome.html +0 -0
- {esp_rainmaker_cli-1.10.0 → esp_rainmaker_cli-1.11.1}/rmaker_cmd/test.py +0 -0
- {esp_rainmaker_cli-1.10.0 → esp_rainmaker_cli-1.11.1}/rmaker_cmd/user.py +0 -0
- {esp_rainmaker_cli-1.10.0 → esp_rainmaker_cli-1.11.1}/rmaker_lib/__init__.py +0 -0
- {esp_rainmaker_cli-1.10.0 → esp_rainmaker_cli-1.11.1}/rmaker_lib/cmd_response.py +0 -0
- {esp_rainmaker_cli-1.10.0 → esp_rainmaker_cli-1.11.1}/rmaker_lib/configmanager.py +0 -0
- {esp_rainmaker_cli-1.10.0 → esp_rainmaker_cli-1.11.1}/rmaker_lib/constants.py +0 -0
- {esp_rainmaker_cli-1.10.0 → esp_rainmaker_cli-1.11.1}/rmaker_lib/device.py +0 -0
- {esp_rainmaker_cli-1.10.0 → esp_rainmaker_cli-1.11.1}/rmaker_lib/envval.py +0 -0
- {esp_rainmaker_cli-1.10.0 → esp_rainmaker_cli-1.11.1}/rmaker_lib/exceptions.py +0 -0
- {esp_rainmaker_cli-1.10.0 → esp_rainmaker_cli-1.11.1}/rmaker_lib/local_control.py +0 -0
- {esp_rainmaker_cli-1.10.0 → esp_rainmaker_cli-1.11.1}/rmaker_lib/logger.py +0 -0
- {esp_rainmaker_cli-1.10.0 → esp_rainmaker_cli-1.11.1}/rmaker_lib/profile_manager.py +0 -0
- {esp_rainmaker_cli-1.10.0 → esp_rainmaker_cli-1.11.1}/rmaker_lib/profile_utils.py +0 -0
- {esp_rainmaker_cli-1.10.0 → esp_rainmaker_cli-1.11.1}/rmaker_lib/schedule_utils.py +0 -0
- {esp_rainmaker_cli-1.10.0 → esp_rainmaker_cli-1.11.1}/rmaker_lib/serverconfig.py +0 -0
- {esp_rainmaker_cli-1.10.0 → esp_rainmaker_cli-1.11.1}/rmaker_lib/service.py +0 -0
- {esp_rainmaker_cli-1.10.0 → esp_rainmaker_cli-1.11.1}/rmaker_lib/session.py +0 -0
- {esp_rainmaker_cli-1.10.0 → esp_rainmaker_cli-1.11.1}/rmaker_lib/simple_local_control.py +0 -0
- {esp_rainmaker_cli-1.10.0 → esp_rainmaker_cli-1.11.1}/rmaker_lib/user.py +0 -0
- {esp_rainmaker_cli-1.10.0 → esp_rainmaker_cli-1.11.1}/rmaker_tools/__init__.py +0 -0
- {esp_rainmaker_cli-1.10.0 → esp_rainmaker_cli-1.11.1}/rmaker_tools/common/__init__.py +0 -0
- {esp_rainmaker_cli-1.10.0 → esp_rainmaker_cli-1.11.1}/rmaker_tools/common/discovery/__init__.py +0 -0
- {esp_rainmaker_cli-1.10.0 → esp_rainmaker_cli-1.11.1}/rmaker_tools/common/discovery/mdns_discovery.py +0 -0
- {esp_rainmaker_cli-1.10.0 → esp_rainmaker_cli-1.11.1}/rmaker_tools/common/prov/__init__.py +0 -0
- {esp_rainmaker_cli-1.10.0 → esp_rainmaker_cli-1.11.1}/rmaker_tools/common/prov/wifi_ctrl.py +0 -0
- {esp_rainmaker_cli-1.10.0 → esp_rainmaker_cli-1.11.1}/rmaker_tools/common/prov/wifi_prov.py +0 -0
- {esp_rainmaker_cli-1.10.0 → esp_rainmaker_cli-1.11.1}/rmaker_tools/common/prov/wifi_scan.py +0 -0
- {esp_rainmaker_cli-1.10.0 → esp_rainmaker_cli-1.11.1}/rmaker_tools/common/security/__init__.py +0 -0
- {esp_rainmaker_cli-1.10.0 → esp_rainmaker_cli-1.11.1}/rmaker_tools/common/security/security.py +0 -0
- {esp_rainmaker_cli-1.10.0 → esp_rainmaker_cli-1.11.1}/rmaker_tools/common/security/security0.py +0 -0
- {esp_rainmaker_cli-1.10.0 → esp_rainmaker_cli-1.11.1}/rmaker_tools/common/security/security1.py +0 -0
- {esp_rainmaker_cli-1.10.0 → esp_rainmaker_cli-1.11.1}/rmaker_tools/common/security/security2.py +0 -0
- {esp_rainmaker_cli-1.10.0 → esp_rainmaker_cli-1.11.1}/rmaker_tools/common/security/srp6a.py +0 -0
- {esp_rainmaker_cli-1.10.0 → esp_rainmaker_cli-1.11.1}/rmaker_tools/common/transport/__init__.py +0 -0
- {esp_rainmaker_cli-1.10.0 → esp_rainmaker_cli-1.11.1}/rmaker_tools/common/transport/ble_cli.py +0 -0
- {esp_rainmaker_cli-1.10.0 → esp_rainmaker_cli-1.11.1}/rmaker_tools/common/transport/transport.py +0 -0
- {esp_rainmaker_cli-1.10.0 → esp_rainmaker_cli-1.11.1}/rmaker_tools/common/transport/transport_ble.py +0 -0
- {esp_rainmaker_cli-1.10.0 → esp_rainmaker_cli-1.11.1}/rmaker_tools/common/transport/transport_console.py +0 -0
- {esp_rainmaker_cli-1.10.0 → esp_rainmaker_cli-1.11.1}/rmaker_tools/common/transport/transport_http.py +0 -0
- {esp_rainmaker_cli-1.10.0 → esp_rainmaker_cli-1.11.1}/rmaker_tools/common/utils/__init__.py +0 -0
- {esp_rainmaker_cli-1.10.0 → esp_rainmaker_cli-1.11.1}/rmaker_tools/common/utils/convenience.py +0 -0
- {esp_rainmaker_cli-1.10.0 → esp_rainmaker_cli-1.11.1}/rmaker_tools/rmaker_claim/__init__.py +0 -0
- {esp_rainmaker_cli-1.10.0 → esp_rainmaker_cli-1.11.1}/rmaker_tools/rmaker_claim/claim_config.py +0 -0
- {esp_rainmaker_cli-1.10.0 → esp_rainmaker_cli-1.11.1}/rmaker_tools/rmaker_local_ctrl/__init__.py +0 -0
- {esp_rainmaker_cli-1.10.0 → esp_rainmaker_cli-1.11.1}/rmaker_tools/rmaker_local_ctrl/constants_pb2.py +0 -0
- {esp_rainmaker_cli-1.10.0 → esp_rainmaker_cli-1.11.1}/rmaker_tools/rmaker_local_ctrl/esp_local_ctrl.py +0 -0
- {esp_rainmaker_cli-1.10.0 → esp_rainmaker_cli-1.11.1}/rmaker_tools/rmaker_local_ctrl/esp_local_ctrl_pb2.py +0 -0
- {esp_rainmaker_cli-1.10.0 → esp_rainmaker_cli-1.11.1}/rmaker_tools/rmaker_local_ctrl/esp_prov.py +0 -0
- {esp_rainmaker_cli-1.10.0 → esp_rainmaker_cli-1.11.1}/rmaker_tools/rmaker_local_ctrl/esp_rainmaker_ctrl.py +0 -0
- {esp_rainmaker_cli-1.10.0 → esp_rainmaker_cli-1.11.1}/rmaker_tools/rmaker_local_ctrl/esp_rmaker_prov_local_ctrl_pb2.py +0 -0
- {esp_rainmaker_cli-1.10.0 → esp_rainmaker_cli-1.11.1}/rmaker_tools/rmaker_local_ctrl/integration.py +0 -0
- {esp_rainmaker_cli-1.10.0 → esp_rainmaker_cli-1.11.1}/rmaker_tools/rmaker_local_ctrl/proto/__init__.py +0 -0
- {esp_rainmaker_cli-1.10.0 → esp_rainmaker_cli-1.11.1}/rmaker_tools/rmaker_local_ctrl/proto/proto_lc.py +0 -0
- {esp_rainmaker_cli-1.10.0 → esp_rainmaker_cli-1.11.1}/rmaker_tools/rmaker_local_ctrl/prov/__init__.py +0 -0
- {esp_rainmaker_cli-1.10.0 → esp_rainmaker_cli-1.11.1}/rmaker_tools/rmaker_local_ctrl/prov/custom_prov.py +0 -0
- {esp_rainmaker_cli-1.10.0 → esp_rainmaker_cli-1.11.1}/rmaker_tools/rmaker_local_ctrl/prov/wifi_ctrl.py +0 -0
- {esp_rainmaker_cli-1.10.0 → esp_rainmaker_cli-1.11.1}/rmaker_tools/rmaker_local_ctrl/raw_config.py +0 -0
- {esp_rainmaker_cli-1.10.0 → esp_rainmaker_cli-1.11.1}/rmaker_tools/rmaker_local_ctrl/raw_params.py +0 -0
- {esp_rainmaker_cli-1.10.0 → esp_rainmaker_cli-1.11.1}/rmaker_tools/rmaker_local_ctrl/sec0_pb2.py +0 -0
- {esp_rainmaker_cli-1.10.0 → esp_rainmaker_cli-1.11.1}/rmaker_tools/rmaker_local_ctrl/sec1_pb2.py +0 -0
- {esp_rainmaker_cli-1.10.0 → esp_rainmaker_cli-1.11.1}/rmaker_tools/rmaker_local_ctrl/sec2_pb2.py +0 -0
- {esp_rainmaker_cli-1.10.0 → esp_rainmaker_cli-1.11.1}/rmaker_tools/rmaker_local_ctrl/security/__init__.py +0 -0
- {esp_rainmaker_cli-1.10.0 → esp_rainmaker_cli-1.11.1}/rmaker_tools/rmaker_local_ctrl/session_pb2.py +0 -0
- {esp_rainmaker_cli-1.10.0 → esp_rainmaker_cli-1.11.1}/rmaker_tools/rmaker_local_ctrl/transport/__init__.py +0 -0
- {esp_rainmaker_cli-1.10.0 → esp_rainmaker_cli-1.11.1}/rmaker_tools/rmaker_local_ctrl/utils/__init__.py +0 -0
- {esp_rainmaker_cli-1.10.0 → esp_rainmaker_cli-1.11.1}/rmaker_tools/rmaker_prov/__init__.py +0 -0
- {esp_rainmaker_cli-1.10.0 → esp_rainmaker_cli-1.11.1}/rmaker_tools/rmaker_prov/config/__init__.py +0 -0
- {esp_rainmaker_cli-1.10.0 → esp_rainmaker_cli-1.11.1}/rmaker_tools/rmaker_prov/config/custom_cloud_config_pb2.py +0 -0
- {esp_rainmaker_cli-1.10.0 → esp_rainmaker_cli-1.11.1}/rmaker_tools/rmaker_prov/config/esp_rmaker_chal_resp_pb2.py +0 -0
- {esp_rainmaker_cli-1.10.0 → esp_rainmaker_cli-1.11.1}/rmaker_tools/rmaker_prov/config/esp_rmaker_claim_pb2.py +0 -0
- {esp_rainmaker_cli-1.10.0 → esp_rainmaker_cli-1.11.1}/rmaker_tools/rmaker_prov/config/esp_rmaker_user_mapping_pb2.py +0 -0
- {esp_rainmaker_cli-1.10.0 → esp_rainmaker_cli-1.11.1}/rmaker_tools/rmaker_prov/proto/__init__.py +0 -0
- {esp_rainmaker_cli-1.10.0 → esp_rainmaker_cli-1.11.1}/rmaker_tools/rmaker_prov/protocomm/__init__.py +0 -0
- {esp_rainmaker_cli-1.10.0 → esp_rainmaker_cli-1.11.1}/rmaker_tools/rmaker_prov/protocomm/python/__init__.py +0 -0
- {esp_rainmaker_cli-1.10.0 → esp_rainmaker_cli-1.11.1}/rmaker_tools/rmaker_prov/protocomm/python/constants_pb2.py +0 -0
- {esp_rainmaker_cli-1.10.0 → esp_rainmaker_cli-1.11.1}/rmaker_tools/rmaker_prov/protocomm/python/sec0_pb2.py +0 -0
- {esp_rainmaker_cli-1.10.0 → esp_rainmaker_cli-1.11.1}/rmaker_tools/rmaker_prov/protocomm/python/sec1_pb2.py +0 -0
- {esp_rainmaker_cli-1.10.0 → esp_rainmaker_cli-1.11.1}/rmaker_tools/rmaker_prov/protocomm/python/sec2_pb2.py +0 -0
- {esp_rainmaker_cli-1.10.0 → esp_rainmaker_cli-1.11.1}/rmaker_tools/rmaker_prov/protocomm/python/session_pb2.py +0 -0
- {esp_rainmaker_cli-1.10.0 → esp_rainmaker_cli-1.11.1}/rmaker_tools/rmaker_prov/prov/__init__.py +0 -0
- {esp_rainmaker_cli-1.10.0 → esp_rainmaker_cli-1.11.1}/rmaker_tools/rmaker_prov/prov/prov_util.py +0 -0
- {esp_rainmaker_cli-1.10.0 → esp_rainmaker_cli-1.11.1}/rmaker_tools/rmaker_prov/prov/user_mapping.py +0 -0
- {esp_rainmaker_cli-1.10.0 → esp_rainmaker_cli-1.11.1}/rmaker_tools/rmaker_prov/security/__init__.py +0 -0
- {esp_rainmaker_cli-1.10.0 → esp_rainmaker_cli-1.11.1}/rmaker_tools/rmaker_prov/transport/__init__.py +0 -0
- {esp_rainmaker_cli-1.10.0 → esp_rainmaker_cli-1.11.1}/rmaker_tools/rmaker_prov/utils/__init__.py +0 -0
- {esp_rainmaker_cli-1.10.0 → esp_rainmaker_cli-1.11.1}/rmaker_tools/rmaker_prov/wifi_provisioning/__init__.py +0 -0
- {esp_rainmaker_cli-1.10.0 → esp_rainmaker_cli-1.11.1}/rmaker_tools/rmaker_prov/wifi_provisioning/python/__init__.py +0 -0
- {esp_rainmaker_cli-1.10.0 → esp_rainmaker_cli-1.11.1}/rmaker_tools/rmaker_prov/wifi_provisioning/python/wifi_config_pb2.py +0 -0
- {esp_rainmaker_cli-1.10.0 → esp_rainmaker_cli-1.11.1}/rmaker_tools/rmaker_prov/wifi_provisioning/python/wifi_constants_pb2.py +0 -0
- {esp_rainmaker_cli-1.10.0 → esp_rainmaker_cli-1.11.1}/rmaker_tools/rmaker_prov/wifi_provisioning/python/wifi_ctrl_pb2.py +0 -0
- {esp_rainmaker_cli-1.10.0 → esp_rainmaker_cli-1.11.1}/rmaker_tools/rmaker_prov/wifi_provisioning/python/wifi_scan_pb2.py +0 -0
- {esp_rainmaker_cli-1.10.0 → esp_rainmaker_cli-1.11.1}/server_cert/__init__.py +0 -0
- {esp_rainmaker_cli-1.10.0 → esp_rainmaker_cli-1.11.1}/server_cert/server_cert.pem +0 -0
- {esp_rainmaker_cli-1.10.0 → esp_rainmaker_cli-1.11.1}/setup.cfg +0 -0
- {esp_rainmaker_cli-1.10.0 → esp_rainmaker_cli-1.11.1}/setup.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: esp-rainmaker-cli
|
|
3
|
-
Version: 1.
|
|
3
|
+
Version: 1.11.1
|
|
4
4
|
Summary: A python utility to perform host based claiming
|
|
5
5
|
Home-page: https://github.com/espressif/esp-rainmaker-cli
|
|
6
6
|
Author: Espressif Systems
|
|
@@ -115,6 +115,13 @@ Changelog
|
|
|
115
115
|
|
|
116
116
|
All major changes to ESP RainMaker CLI will be documented in this file.
|
|
117
117
|
|
|
118
|
+
## [1.11.1] - 10-Feb-2026
|
|
119
|
+
- Add support for ecdsa in claiming and make it as default, with an option to fall back to the earlier rsa scheme.
|
|
120
|
+
|
|
121
|
+
## [1.11.0] - 09-Feb-2026
|
|
122
|
+
- Add support for Tags and Metadata during provisioning and also after mapping a user. Check `provision`
|
|
123
|
+
and `node` commands
|
|
124
|
+
|
|
118
125
|
## [1.10.0] - 22-Jan-2026
|
|
119
126
|
### Added
|
|
120
127
|
- BLE local control support for `getparams`, `setparams`, and `getnodeconfig` commands during provisioning phase:
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: esp-rainmaker-cli
|
|
3
|
-
Version: 1.
|
|
3
|
+
Version: 1.11.1
|
|
4
4
|
Summary: A python utility to perform host based claiming
|
|
5
5
|
Home-page: https://github.com/espressif/esp-rainmaker-cli
|
|
6
6
|
Author: Espressif Systems
|
|
@@ -115,6 +115,13 @@ Changelog
|
|
|
115
115
|
|
|
116
116
|
All major changes to ESP RainMaker CLI will be documented in this file.
|
|
117
117
|
|
|
118
|
+
## [1.11.1] - 10-Feb-2026
|
|
119
|
+
- Add support for ecdsa in claiming and make it as default, with an option to fall back to the earlier rsa scheme.
|
|
120
|
+
|
|
121
|
+
## [1.11.0] - 09-Feb-2026
|
|
122
|
+
- Add support for Tags and Metadata during provisioning and also after mapping a user. Check `provision`
|
|
123
|
+
and `node` commands
|
|
124
|
+
|
|
118
125
|
## [1.10.0] - 22-Jan-2026
|
|
119
126
|
### Added
|
|
120
127
|
- BLE local control support for `getparams`, `setparams`, and `getnodeconfig` commands during provisioning phase:
|
|
@@ -7,6 +7,7 @@
|
|
|
7
7
|
import sys
|
|
8
8
|
import argparse
|
|
9
9
|
from rmaker_cmd.node import *
|
|
10
|
+
from rmaker_cmd.node import node_add_tags, node_remove_tags, node_set_metadata, node_delete_metadata
|
|
10
11
|
from rmaker_cmd.user import signup, login, forgot_password,\
|
|
11
12
|
get_user_details, logout, set_region_configuration, \
|
|
12
13
|
profile_list, profile_current, profile_switch, profile_add, profile_remove, delete_user
|
|
@@ -483,6 +484,16 @@ def main():
|
|
|
483
484
|
help='Do NOT disable challenge-response on device after successful mapping.\n'
|
|
484
485
|
'Overrides the transport-specific default behavior.')
|
|
485
486
|
|
|
487
|
+
provision_parser.add_argument('--tags',
|
|
488
|
+
type=str,
|
|
489
|
+
help='Comma-separated list of tags to attach during node mapping\n'
|
|
490
|
+
'(e.g., "location:pune,name:espressif"). Tags must be in key:value format.')
|
|
491
|
+
|
|
492
|
+
provision_parser.add_argument('--metadata',
|
|
493
|
+
type=str,
|
|
494
|
+
help='Metadata as JSON string to attach during node mapping\n'
|
|
495
|
+
'(e.g., \'{"serial_no": "abc123", "region": "us"}\')')
|
|
496
|
+
|
|
486
497
|
add_profile_argument(provision_parser)
|
|
487
498
|
provision_parser.set_defaults(func=provision)
|
|
488
499
|
|
|
@@ -526,6 +537,14 @@ def main():
|
|
|
526
537
|
type=str,
|
|
527
538
|
help='Special RainMaker Node type (e.g., "camera").')
|
|
528
539
|
|
|
540
|
+
claim_parser.add_argument("--key_type", metavar='<key_type>',
|
|
541
|
+
default="ecdsa",
|
|
542
|
+
choices=['rsa', 'ecdsa'],
|
|
543
|
+
help='Cryptographic key type for node certificate (Not applicable for Matter claiming)\n'
|
|
544
|
+
'ecdsa: ECDSA P-256 (smaller, faster)\n'
|
|
545
|
+
'rsa: RSA 2048-bit (legacy)\n'
|
|
546
|
+
'Default: ecdsa')
|
|
547
|
+
|
|
529
548
|
add_profile_argument(claim_parser)
|
|
530
549
|
claim_parser.set_defaults(func=claim_node, parser=claim_parser)
|
|
531
550
|
|
|
@@ -839,6 +858,89 @@ def main():
|
|
|
839
858
|
add_profile_argument(group_list_nodes_parser)
|
|
840
859
|
group_list_nodes_parser.set_defaults(func=group_list_nodes)
|
|
841
860
|
|
|
861
|
+
# Node Management (tags, metadata)
|
|
862
|
+
node_parser = subparsers.add_parser('node',
|
|
863
|
+
help='Manage node properties (tags, metadata)')
|
|
864
|
+
node_parser.set_defaults(func=lambda vars=None: node_parser.print_help())
|
|
865
|
+
node_subparsers = node_parser.add_subparsers(dest='node_command', help='Node operations')
|
|
866
|
+
|
|
867
|
+
# node add-tags
|
|
868
|
+
node_add_tags_parser = node_subparsers.add_parser('add-tags',
|
|
869
|
+
help='Add tags to a node',
|
|
870
|
+
description='Add tags to a node. Tags must be in key:value format.')
|
|
871
|
+
node_add_tags_parser.add_argument('nodeid',
|
|
872
|
+
type=str,
|
|
873
|
+
metavar='<nodeid>',
|
|
874
|
+
help='Node ID for the node')
|
|
875
|
+
node_add_tags_parser.add_argument('--tags',
|
|
876
|
+
type=str,
|
|
877
|
+
required=True,
|
|
878
|
+
metavar='<tags>',
|
|
879
|
+
help='Comma-separated list of tags in key:value format\n'
|
|
880
|
+
'(e.g., "location:pune,name:espressif")')
|
|
881
|
+
add_profile_argument(node_add_tags_parser)
|
|
882
|
+
node_add_tags_parser.set_defaults(func=node_add_tags)
|
|
883
|
+
|
|
884
|
+
# node remove-tags
|
|
885
|
+
node_remove_tags_parser = node_subparsers.add_parser('remove-tags',
|
|
886
|
+
help='Remove tags from a node',
|
|
887
|
+
description='Remove specific tags from a node.')
|
|
888
|
+
node_remove_tags_parser.add_argument('nodeid',
|
|
889
|
+
type=str,
|
|
890
|
+
metavar='<nodeid>',
|
|
891
|
+
help='Node ID for the node')
|
|
892
|
+
node_remove_tags_parser.add_argument('--tags',
|
|
893
|
+
type=str,
|
|
894
|
+
required=True,
|
|
895
|
+
metavar='<tags>',
|
|
896
|
+
help='Comma-separated list of tags to remove\n'
|
|
897
|
+
'(e.g., "location:pune,name:espressif")')
|
|
898
|
+
add_profile_argument(node_remove_tags_parser)
|
|
899
|
+
node_remove_tags_parser.set_defaults(func=node_remove_tags)
|
|
900
|
+
|
|
901
|
+
# node set-metadata
|
|
902
|
+
node_set_metadata_parser = node_subparsers.add_parser('set-metadata',
|
|
903
|
+
help='Set or update metadata for a node',
|
|
904
|
+
description='Set or update metadata for a node.\n'
|
|
905
|
+
'Follows shadow-style merge rules:\n'
|
|
906
|
+
' - New keys are added, existing keys are updated\n'
|
|
907
|
+
' - Set a key to null to delete it\n'
|
|
908
|
+
' - Arrays are overwritten (not merged)')
|
|
909
|
+
node_set_metadata_parser.add_argument('nodeid',
|
|
910
|
+
type=str,
|
|
911
|
+
metavar='<nodeid>',
|
|
912
|
+
help='Node ID for the node')
|
|
913
|
+
node_set_metadata_data_group = node_set_metadata_parser.add_mutually_exclusive_group(required=True)
|
|
914
|
+
node_set_metadata_data_group.add_argument('--data',
|
|
915
|
+
type=str,
|
|
916
|
+
metavar='<json>',
|
|
917
|
+
help='Metadata as JSON string\n'
|
|
918
|
+
'(e.g., \'{"key": "value", "region": "us"}\')')
|
|
919
|
+
node_set_metadata_data_group.add_argument('--filepath',
|
|
920
|
+
type=str,
|
|
921
|
+
metavar='<path>',
|
|
922
|
+
help='Path to JSON file containing metadata')
|
|
923
|
+
add_profile_argument(node_set_metadata_parser)
|
|
924
|
+
node_set_metadata_parser.set_defaults(func=node_set_metadata)
|
|
925
|
+
|
|
926
|
+
# node delete-metadata
|
|
927
|
+
node_delete_metadata_parser = node_subparsers.add_parser('delete-metadata',
|
|
928
|
+
help='Delete metadata from a node',
|
|
929
|
+
description='Delete metadata from a node.\n'
|
|
930
|
+
'Without --key, deletes all metadata.\n'
|
|
931
|
+
'With --key, deletes only the specified key(s).')
|
|
932
|
+
node_delete_metadata_parser.add_argument('nodeid',
|
|
933
|
+
type=str,
|
|
934
|
+
metavar='<nodeid>',
|
|
935
|
+
help='Node ID for the node')
|
|
936
|
+
node_delete_metadata_parser.add_argument('--key',
|
|
937
|
+
type=str,
|
|
938
|
+
metavar='<keys>',
|
|
939
|
+
help='Comma-separated list of metadata keys to delete\n'
|
|
940
|
+
'(e.g., "region,name"). If omitted, all metadata is deleted.')
|
|
941
|
+
add_profile_argument(node_delete_metadata_parser)
|
|
942
|
+
node_delete_metadata_parser.set_defaults(func=node_delete_metadata)
|
|
943
|
+
|
|
842
944
|
# Raw API command
|
|
843
945
|
raw_api_parser = subparsers.add_parser('raw-api',
|
|
844
946
|
help='Make raw API calls to RainMaker backend',
|
|
@@ -1664,7 +1664,7 @@ def claim_node(vars=None):
|
|
|
1664
1664
|
sys.exit('Invalid MAC address.')
|
|
1665
1665
|
|
|
1666
1666
|
from rmaker_tools.rmaker_claim.claim import claim
|
|
1667
|
-
claim(port=vars['port'], node_platform=vars['platform'], mac_addr=vars['mac'], flash_address=vars['addr'], matter=vars['matter'], out_dir=vars['outdir'], node_type=vars['type'])
|
|
1667
|
+
claim(port=vars['port'], node_platform=vars['platform'], mac_addr=vars['mac'], flash_address=vars['addr'], matter=vars['matter'], out_dir=vars['outdir'], node_type=vars['type'], key_type=vars['key_type'])
|
|
1668
1668
|
except Exception as claim_err:
|
|
1669
1669
|
log.error(claim_err)
|
|
1670
1670
|
return
|
|
@@ -1878,3 +1878,203 @@ def raw_api_call(vars=None):
|
|
|
1878
1878
|
traceback.print_exc()
|
|
1879
1879
|
return
|
|
1880
1880
|
|
|
1881
|
+
|
|
1882
|
+
def node_add_tags(vars=None):
|
|
1883
|
+
"""
|
|
1884
|
+
Add tags to a node.
|
|
1885
|
+
|
|
1886
|
+
:param vars: Parameters:
|
|
1887
|
+
`nodeid` as key - Node ID for the node
|
|
1888
|
+
`tags` as key - Comma-separated list of tags (key:value format)
|
|
1889
|
+
`profile` as key - Profile to use for the operation
|
|
1890
|
+
:type vars: dict | None
|
|
1891
|
+
|
|
1892
|
+
:return: None on Success
|
|
1893
|
+
:rtype: None
|
|
1894
|
+
"""
|
|
1895
|
+
try:
|
|
1896
|
+
s = get_session_with_profile(vars or {})
|
|
1897
|
+
node_id = vars.get('nodeid')
|
|
1898
|
+
tags_str = vars.get('tags')
|
|
1899
|
+
|
|
1900
|
+
if not node_id:
|
|
1901
|
+
print("Error: Node ID is required.")
|
|
1902
|
+
return
|
|
1903
|
+
if not tags_str:
|
|
1904
|
+
print("Error: Tags are required.")
|
|
1905
|
+
return
|
|
1906
|
+
|
|
1907
|
+
# Parse comma-separated tags
|
|
1908
|
+
tags = [t.strip() for t in tags_str.split(',') if t.strip()]
|
|
1909
|
+
|
|
1910
|
+
# Validate tag format (key:value)
|
|
1911
|
+
for tag in tags:
|
|
1912
|
+
if ':' not in tag:
|
|
1913
|
+
print(f"Error: Invalid tag format '{tag}'. Tags must be in key:value format.")
|
|
1914
|
+
return
|
|
1915
|
+
|
|
1916
|
+
n = node.Node(node_id, s)
|
|
1917
|
+
response = n.update_node_tags(tags)
|
|
1918
|
+
|
|
1919
|
+
if response.get('status') == 'success':
|
|
1920
|
+
print(f"Tags added to node {node_id} successfully.")
|
|
1921
|
+
else:
|
|
1922
|
+
description = response.get('description', 'Unknown error')
|
|
1923
|
+
print(f"Failed to add tags: {description}")
|
|
1924
|
+
|
|
1925
|
+
except Exception as e:
|
|
1926
|
+
log.error(f"Error adding tags: {e}")
|
|
1927
|
+
|
|
1928
|
+
|
|
1929
|
+
def node_remove_tags(vars=None):
|
|
1930
|
+
"""
|
|
1931
|
+
Remove tags from a node.
|
|
1932
|
+
|
|
1933
|
+
:param vars: Parameters:
|
|
1934
|
+
`nodeid` as key - Node ID for the node
|
|
1935
|
+
`tags` as key - Comma-separated list of tags to remove (key:value format)
|
|
1936
|
+
`profile` as key - Profile to use for the operation
|
|
1937
|
+
:type vars: dict | None
|
|
1938
|
+
|
|
1939
|
+
:return: None on Success
|
|
1940
|
+
:rtype: None
|
|
1941
|
+
"""
|
|
1942
|
+
try:
|
|
1943
|
+
s = get_session_with_profile(vars or {})
|
|
1944
|
+
node_id = vars.get('nodeid')
|
|
1945
|
+
tags_str = vars.get('tags')
|
|
1946
|
+
|
|
1947
|
+
if not node_id:
|
|
1948
|
+
print("Error: Node ID is required.")
|
|
1949
|
+
return
|
|
1950
|
+
if not tags_str:
|
|
1951
|
+
print("Error: Tags are required.")
|
|
1952
|
+
return
|
|
1953
|
+
|
|
1954
|
+
# Parse comma-separated tags
|
|
1955
|
+
tags = [t.strip() for t in tags_str.split(',') if t.strip()]
|
|
1956
|
+
|
|
1957
|
+
n = node.Node(node_id, s)
|
|
1958
|
+
response = n.remove_node_tags(tags)
|
|
1959
|
+
|
|
1960
|
+
if response.get('status') == 'success':
|
|
1961
|
+
print(f"Tags removed from node {node_id} successfully.")
|
|
1962
|
+
else:
|
|
1963
|
+
description = response.get('description', 'Unknown error')
|
|
1964
|
+
print(f"Failed to remove tags: {description}")
|
|
1965
|
+
|
|
1966
|
+
except Exception as e:
|
|
1967
|
+
log.error(f"Error removing tags: {e}")
|
|
1968
|
+
|
|
1969
|
+
|
|
1970
|
+
def node_set_metadata(vars=None):
|
|
1971
|
+
"""
|
|
1972
|
+
Set or update metadata for a node. Follows shadow-style merge rules:
|
|
1973
|
+
- New keys are added, existing keys are updated
|
|
1974
|
+
- To delete a specific key, set its value to null in the JSON
|
|
1975
|
+
|
|
1976
|
+
:param vars: Parameters:
|
|
1977
|
+
`nodeid` as key - Node ID for the node
|
|
1978
|
+
`data` as key - JSON string of metadata to set
|
|
1979
|
+
`filepath` as key - Path to JSON file containing metadata
|
|
1980
|
+
`profile` as key - Profile to use for the operation
|
|
1981
|
+
:type vars: dict | None
|
|
1982
|
+
|
|
1983
|
+
:return: None on Success
|
|
1984
|
+
:rtype: None
|
|
1985
|
+
"""
|
|
1986
|
+
try:
|
|
1987
|
+
s = get_session_with_profile(vars or {})
|
|
1988
|
+
node_id = vars.get('nodeid')
|
|
1989
|
+
|
|
1990
|
+
if not node_id:
|
|
1991
|
+
print("Error: Node ID is required.")
|
|
1992
|
+
return
|
|
1993
|
+
|
|
1994
|
+
# Parse metadata from --data or --filepath
|
|
1995
|
+
data_str = vars.get('data')
|
|
1996
|
+
filepath = vars.get('filepath')
|
|
1997
|
+
|
|
1998
|
+
if not data_str and not filepath:
|
|
1999
|
+
print("Error: Either --data or --filepath is required.")
|
|
2000
|
+
return
|
|
2001
|
+
|
|
2002
|
+
metadata = None
|
|
2003
|
+
if data_str:
|
|
2004
|
+
try:
|
|
2005
|
+
metadata = json.loads(data_str)
|
|
2006
|
+
except json.JSONDecodeError as e:
|
|
2007
|
+
print(f"Error: Invalid JSON in --data: {e}")
|
|
2008
|
+
return
|
|
2009
|
+
elif filepath:
|
|
2010
|
+
try:
|
|
2011
|
+
with open(filepath, 'r', encoding='utf-8') as f:
|
|
2012
|
+
metadata = json.load(f)
|
|
2013
|
+
except FileNotFoundError:
|
|
2014
|
+
print(f"Error: File not found: {filepath}")
|
|
2015
|
+
return
|
|
2016
|
+
except json.JSONDecodeError as e:
|
|
2017
|
+
print(f"Error: Invalid JSON in file {filepath}: {e}")
|
|
2018
|
+
return
|
|
2019
|
+
|
|
2020
|
+
n = node.Node(node_id, s)
|
|
2021
|
+
response = n.update_node_metadata(metadata)
|
|
2022
|
+
|
|
2023
|
+
if response.get('status') == 'success':
|
|
2024
|
+
print(f"Metadata updated for node {node_id} successfully.")
|
|
2025
|
+
else:
|
|
2026
|
+
description = response.get('description', 'Unknown error')
|
|
2027
|
+
print(f"Failed to update metadata: {description}")
|
|
2028
|
+
|
|
2029
|
+
except Exception as e:
|
|
2030
|
+
log.error(f"Error updating metadata: {e}")
|
|
2031
|
+
|
|
2032
|
+
|
|
2033
|
+
def node_delete_metadata(vars=None):
|
|
2034
|
+
"""
|
|
2035
|
+
Delete metadata from a node.
|
|
2036
|
+
Without --key, deletes all metadata.
|
|
2037
|
+
With --key, deletes only the specified key(s).
|
|
2038
|
+
|
|
2039
|
+
:param vars: Parameters:
|
|
2040
|
+
`nodeid` as key - Node ID for the node
|
|
2041
|
+
`key` as key - Comma-separated list of metadata keys to delete (optional)
|
|
2042
|
+
`profile` as key - Profile to use for the operation
|
|
2043
|
+
:type vars: dict | None
|
|
2044
|
+
|
|
2045
|
+
:return: None on Success
|
|
2046
|
+
:rtype: None
|
|
2047
|
+
"""
|
|
2048
|
+
try:
|
|
2049
|
+
s = get_session_with_profile(vars or {})
|
|
2050
|
+
node_id = vars.get('nodeid')
|
|
2051
|
+
|
|
2052
|
+
if not node_id:
|
|
2053
|
+
print("Error: Node ID is required.")
|
|
2054
|
+
return
|
|
2055
|
+
|
|
2056
|
+
key_str = vars.get('key')
|
|
2057
|
+
|
|
2058
|
+
n = node.Node(node_id, s)
|
|
2059
|
+
|
|
2060
|
+
if key_str:
|
|
2061
|
+
# Delete specific keys by setting them to None
|
|
2062
|
+
keys = [k.strip() for k in key_str.split(',') if k.strip()]
|
|
2063
|
+
metadata = {k: None for k in keys}
|
|
2064
|
+
response = n.update_node_metadata(metadata)
|
|
2065
|
+
key_list = ', '.join(keys)
|
|
2066
|
+
success_msg = f"Metadata key(s) '{key_list}' deleted from node {node_id} successfully."
|
|
2067
|
+
else:
|
|
2068
|
+
# Delete all metadata by sending null
|
|
2069
|
+
response = n.update_node_metadata(None)
|
|
2070
|
+
success_msg = f"All metadata deleted from node {node_id} successfully."
|
|
2071
|
+
|
|
2072
|
+
if response.get('status') == 'success':
|
|
2073
|
+
print(success_msg)
|
|
2074
|
+
else:
|
|
2075
|
+
description = response.get('description', 'Unknown error')
|
|
2076
|
+
print(f"Failed to delete metadata: {description}")
|
|
2077
|
+
|
|
2078
|
+
except Exception as e:
|
|
2079
|
+
log.error(f"Error deleting metadata: {e}")
|
|
2080
|
+
|
|
@@ -123,6 +123,19 @@ def provision(vars=None):
|
|
|
123
123
|
passphrase = vars.get('passphrase')
|
|
124
124
|
no_wifi = vars.get('no_wifi', False)
|
|
125
125
|
|
|
126
|
+
# Parse tags and metadata for node mapping
|
|
127
|
+
mapping_tags = None
|
|
128
|
+
mapping_metadata = None
|
|
129
|
+
tags_str = vars.get('tags')
|
|
130
|
+
metadata_str = vars.get('metadata')
|
|
131
|
+
if tags_str:
|
|
132
|
+
mapping_tags = [t.strip() for t in tags_str.split(',') if t.strip()]
|
|
133
|
+
if metadata_str:
|
|
134
|
+
try:
|
|
135
|
+
mapping_metadata = json.loads(metadata_str)
|
|
136
|
+
except json.JSONDecodeError as e:
|
|
137
|
+
raise ValueError(f"Invalid JSON in --metadata: {e}")
|
|
138
|
+
|
|
126
139
|
# Generate secret key for user-node mapping
|
|
127
140
|
secret_key = str(uuid.uuid4())
|
|
128
141
|
|
|
@@ -182,7 +195,9 @@ def provision(vars=None):
|
|
|
182
195
|
sec_ver_override=sec_ver,
|
|
183
196
|
discovery_timeout=discovery_timeout,
|
|
184
197
|
interactive=True,
|
|
185
|
-
disable_on_success=disable_chal_resp
|
|
198
|
+
disable_on_success=disable_chal_resp,
|
|
199
|
+
tags=mapping_tags,
|
|
200
|
+
metadata=mapping_metadata
|
|
186
201
|
)
|
|
187
202
|
|
|
188
203
|
if success and node_id:
|
|
@@ -220,7 +235,9 @@ def provision(vars=None):
|
|
|
220
235
|
session=curr_session,
|
|
221
236
|
no_retry=vars.get('no_retry', False),
|
|
222
237
|
no_wifi=no_wifi,
|
|
223
|
-
disable_chal_resp=disable_chal_resp
|
|
238
|
+
disable_chal_resp=disable_chal_resp,
|
|
239
|
+
tags=mapping_tags,
|
|
240
|
+
metadata=mapping_metadata
|
|
224
241
|
)
|
|
225
242
|
except RuntimeError as claim_err:
|
|
226
243
|
# Handle claim requirement error specifically
|
|
@@ -272,7 +289,7 @@ def provision(vars=None):
|
|
|
272
289
|
try:
|
|
273
290
|
log.info('Adding node to user account...')
|
|
274
291
|
node_obj = node.Node(node_id, curr_session)
|
|
275
|
-
request_id = node_obj.add_user_node_mapping(secret_key)
|
|
292
|
+
request_id = node_obj.add_user_node_mapping(secret_key, tags=mapping_tags, metadata=mapping_metadata)
|
|
276
293
|
if not request_id:
|
|
277
294
|
print(f'⚠️ Warning: Node provisioned but failed to add to account')
|
|
278
295
|
log.warning('add_user_node_mapping returned None')
|
|
@@ -340,7 +340,7 @@ class Node:
|
|
|
340
340
|
log.info(f"Updating parameters for {len(node_params_list)} nodes")
|
|
341
341
|
return Node._set_params_api(node_params_list, session.config, session.request_header)
|
|
342
342
|
|
|
343
|
-
def __user_node_mapping(self, secret_key, operation):
|
|
343
|
+
def __user_node_mapping(self, secret_key, operation, tags=None, metadata=None):
|
|
344
344
|
"""
|
|
345
345
|
Add or remove the user node mapping.
|
|
346
346
|
|
|
@@ -352,6 +352,12 @@ class Node:
|
|
|
352
352
|
'add' or 'remove'
|
|
353
353
|
:type operation: str
|
|
354
354
|
|
|
355
|
+
:param tags: Optional list of tags to attach during mapping (format: ["key:value", ...])
|
|
356
|
+
:type tags: list | None
|
|
357
|
+
|
|
358
|
+
:param metadata: Optional metadata dict to attach during mapping
|
|
359
|
+
:type metadata: dict | None
|
|
360
|
+
|
|
355
361
|
:raises NetworkError: If there is a network connection issue
|
|
356
362
|
while adding user node mapping
|
|
357
363
|
:raises Exception: If there is an HTTP issue or JSON format issue
|
|
@@ -371,6 +377,11 @@ class Node:
|
|
|
371
377
|
'operation': operation
|
|
372
378
|
}
|
|
373
379
|
|
|
380
|
+
if tags:
|
|
381
|
+
request_payload['tags'] = tags
|
|
382
|
+
if metadata:
|
|
383
|
+
request_payload['metadata'] = metadata
|
|
384
|
+
|
|
374
385
|
# Get fresh token (will refresh if expired) before making API call
|
|
375
386
|
# This prevents "Unauthorized" errors if token expired during provisioning
|
|
376
387
|
try:
|
|
@@ -419,7 +430,7 @@ class Node:
|
|
|
419
430
|
return response['request_id']
|
|
420
431
|
return None
|
|
421
432
|
|
|
422
|
-
def add_user_node_mapping(self, secret_key):
|
|
433
|
+
def add_user_node_mapping(self, secret_key, tags=None, metadata=None):
|
|
423
434
|
"""
|
|
424
435
|
Add user node mapping.
|
|
425
436
|
|
|
@@ -427,6 +438,12 @@ class Node:
|
|
|
427
438
|
used for User-Node mapping
|
|
428
439
|
:type secret_key: str
|
|
429
440
|
|
|
441
|
+
:param tags: Optional list of tags to attach during mapping (format: ["key:value", ...])
|
|
442
|
+
:type tags: list | None
|
|
443
|
+
|
|
444
|
+
:param metadata: Optional metadata dict to attach during mapping
|
|
445
|
+
:type metadata: dict | None
|
|
446
|
+
|
|
430
447
|
:raises NetworkError: If there is a network connection issue while
|
|
431
448
|
adding user node mapping
|
|
432
449
|
:raises Exception: If there is an HTTP issue while
|
|
@@ -438,7 +455,7 @@ class Node:
|
|
|
438
455
|
"""
|
|
439
456
|
log.info("Adding user node mapping request with nodeid : " +
|
|
440
457
|
self.__nodeid)
|
|
441
|
-
return self.__user_node_mapping(secret_key, 'add')
|
|
458
|
+
return self.__user_node_mapping(secret_key, 'add', tags=tags, metadata=metadata)
|
|
442
459
|
|
|
443
460
|
def remove_user_node_mapping(self):
|
|
444
461
|
"""
|
|
@@ -845,6 +862,163 @@ class Node:
|
|
|
845
862
|
raise get_nodes_params_err
|
|
846
863
|
|
|
847
864
|
response = json.loads(response.text)
|
|
848
|
-
log.debug("Received
|
|
865
|
+
log.debug("Received sharing requests successfully.")
|
|
866
|
+
|
|
867
|
+
return response
|
|
868
|
+
|
|
869
|
+
def update_node_tags(self, tags):
|
|
870
|
+
"""
|
|
871
|
+
Add tags to a node.
|
|
872
|
+
Uses PUT /v1/user/nodes?node_id=<node_id>
|
|
873
|
+
|
|
874
|
+
:param tags: List of tags to add (format: ["key:value", ...])
|
|
875
|
+
:type tags: list
|
|
876
|
+
|
|
877
|
+
:raises NetworkError: If there is a network connection issue
|
|
878
|
+
:raises Exception: If there is an HTTP issue while updating tags
|
|
879
|
+
|
|
880
|
+
:return: API response on Success
|
|
881
|
+
:rtype: dict
|
|
882
|
+
"""
|
|
883
|
+
socket.setdefaulttimeout(10)
|
|
884
|
+
log.info("Adding tags to node : " + self.__nodeid)
|
|
885
|
+
path = 'user/nodes'
|
|
886
|
+
query_parameters = 'node_id=' + self.__nodeid
|
|
887
|
+
url = self.config.get_host() + path + '?' + query_parameters
|
|
888
|
+
|
|
889
|
+
payload = {'tags': tags}
|
|
890
|
+
|
|
891
|
+
try:
|
|
892
|
+
log.debug("Update node tags request url : " + url)
|
|
893
|
+
log.debug("Update node tags request payload : " + json.dumps(payload))
|
|
894
|
+
response = requests.put(url=url,
|
|
895
|
+
headers=self.request_header,
|
|
896
|
+
json=payload,
|
|
897
|
+
verify=configmanager.CERT_FILE,
|
|
898
|
+
timeout=(5.0, 5.0))
|
|
899
|
+
log.debug("Update node tags response : " + response.text)
|
|
900
|
+
response.raise_for_status()
|
|
901
|
+
|
|
902
|
+
except HTTPError as http_err:
|
|
903
|
+
log.debug(http_err)
|
|
904
|
+
raise HttpErrorResponse(response.json())
|
|
905
|
+
except requests.exceptions.SSLError:
|
|
906
|
+
raise SSLError
|
|
907
|
+
except requests.exceptions.ConnectionError:
|
|
908
|
+
raise NetworkError
|
|
909
|
+
except Timeout as time_err:
|
|
910
|
+
log.debug(time_err)
|
|
911
|
+
raise RequestTimeoutError
|
|
912
|
+
except RequestException as req_err:
|
|
913
|
+
log.debug(req_err)
|
|
914
|
+
raise req_err
|
|
915
|
+
|
|
916
|
+
response = json.loads(response.text)
|
|
917
|
+
log.info("Added tags to node successfully.")
|
|
918
|
+
return response
|
|
919
|
+
|
|
920
|
+
def remove_node_tags(self, tags):
|
|
921
|
+
"""
|
|
922
|
+
Remove tags from a node.
|
|
923
|
+
Uses DELETE /v1/user/nodes?node_id=<node_id>
|
|
924
|
+
|
|
925
|
+
:param tags: List of tags to remove (format: ["key:value", ...])
|
|
926
|
+
:type tags: list
|
|
927
|
+
|
|
928
|
+
:raises NetworkError: If there is a network connection issue
|
|
929
|
+
:raises Exception: If there is an HTTP issue while removing tags
|
|
930
|
+
|
|
931
|
+
:return: API response on Success
|
|
932
|
+
:rtype: dict
|
|
933
|
+
"""
|
|
934
|
+
socket.setdefaulttimeout(10)
|
|
935
|
+
log.info("Removing tags from node : " + self.__nodeid)
|
|
936
|
+
path = 'user/nodes'
|
|
937
|
+
query_parameters = 'node_id=' + self.__nodeid
|
|
938
|
+
url = self.config.get_host() + path + '?' + query_parameters
|
|
849
939
|
|
|
940
|
+
payload = {'tags': tags}
|
|
941
|
+
|
|
942
|
+
try:
|
|
943
|
+
log.debug("Remove node tags request url : " + url)
|
|
944
|
+
log.debug("Remove node tags request payload : " + json.dumps(payload))
|
|
945
|
+
response = requests.delete(url=url,
|
|
946
|
+
headers=self.request_header,
|
|
947
|
+
json=payload,
|
|
948
|
+
verify=configmanager.CERT_FILE,
|
|
949
|
+
timeout=(5.0, 5.0))
|
|
950
|
+
log.debug("Remove node tags response : " + response.text)
|
|
951
|
+
response.raise_for_status()
|
|
952
|
+
|
|
953
|
+
except HTTPError as http_err:
|
|
954
|
+
log.debug(http_err)
|
|
955
|
+
raise HttpErrorResponse(response.json())
|
|
956
|
+
except requests.exceptions.SSLError:
|
|
957
|
+
raise SSLError
|
|
958
|
+
except requests.exceptions.ConnectionError:
|
|
959
|
+
raise NetworkError
|
|
960
|
+
except Timeout as time_err:
|
|
961
|
+
log.debug(time_err)
|
|
962
|
+
raise RequestTimeoutError
|
|
963
|
+
except RequestException as req_err:
|
|
964
|
+
log.debug(req_err)
|
|
965
|
+
raise req_err
|
|
966
|
+
|
|
967
|
+
response = json.loads(response.text)
|
|
968
|
+
log.info("Removed tags from node successfully.")
|
|
969
|
+
return response
|
|
970
|
+
|
|
971
|
+
def update_node_metadata(self, metadata):
|
|
972
|
+
"""
|
|
973
|
+
Update metadata for a node. Follows shadow-style merge rules:
|
|
974
|
+
- {"key": value} upserts that key
|
|
975
|
+
- {"key": null} deletes that key
|
|
976
|
+
- null (top-level) deletes all metadata
|
|
977
|
+
|
|
978
|
+
Uses PUT /v1/user/nodes?node_id=<node_id>
|
|
979
|
+
|
|
980
|
+
:param metadata: Metadata dict to set/update, or None to delete all
|
|
981
|
+
:type metadata: dict | None
|
|
982
|
+
|
|
983
|
+
:raises NetworkError: If there is a network connection issue
|
|
984
|
+
:raises Exception: If there is an HTTP issue while updating metadata
|
|
985
|
+
|
|
986
|
+
:return: API response on Success
|
|
987
|
+
:rtype: dict
|
|
988
|
+
"""
|
|
989
|
+
socket.setdefaulttimeout(10)
|
|
990
|
+
log.info("Updating metadata for node : " + self.__nodeid)
|
|
991
|
+
path = 'user/nodes'
|
|
992
|
+
query_parameters = 'node_id=' + self.__nodeid
|
|
993
|
+
url = self.config.get_host() + path + '?' + query_parameters
|
|
994
|
+
|
|
995
|
+
payload = {'metadata': metadata}
|
|
996
|
+
|
|
997
|
+
try:
|
|
998
|
+
log.debug("Update node metadata request url : " + url)
|
|
999
|
+
log.debug("Update node metadata request payload : " + json.dumps(payload))
|
|
1000
|
+
response = requests.put(url=url,
|
|
1001
|
+
headers=self.request_header,
|
|
1002
|
+
json=payload,
|
|
1003
|
+
verify=configmanager.CERT_FILE,
|
|
1004
|
+
timeout=(5.0, 5.0))
|
|
1005
|
+
log.debug("Update node metadata response : " + response.text)
|
|
1006
|
+
response.raise_for_status()
|
|
1007
|
+
|
|
1008
|
+
except HTTPError as http_err:
|
|
1009
|
+
log.debug(http_err)
|
|
1010
|
+
raise HttpErrorResponse(response.json())
|
|
1011
|
+
except requests.exceptions.SSLError:
|
|
1012
|
+
raise SSLError
|
|
1013
|
+
except requests.exceptions.ConnectionError:
|
|
1014
|
+
raise NetworkError
|
|
1015
|
+
except Timeout as time_err:
|
|
1016
|
+
log.debug(time_err)
|
|
1017
|
+
raise RequestTimeoutError
|
|
1018
|
+
except RequestException as req_err:
|
|
1019
|
+
log.debug(req_err)
|
|
1020
|
+
raise req_err
|
|
1021
|
+
|
|
1022
|
+
response = json.loads(response.text)
|
|
1023
|
+
log.info("Updated node metadata successfully.")
|
|
850
1024
|
return response
|