def get_params(vars=None): """ Get parameters of the node. :param vars: `nodeid` as key - Node ID for the node, defaults to `None` :type vars: dict | None :raises Exception: If there is an HTTP issue while getting params or JSON format issue in HTTP response :return: None on Success :rtype: None """ try: n = node.Node(vars['nodeid'], session.Session()) params = n.get_node_params() except SSLError: log.error(SSLError()) except NetworkError as conn_err: print(conn_err) log.warn(conn_err) except Exception as get_params_err: log.error(get_params_err) else: if params is None: log.error('Node status not updated.') return else: print(json.dumps(params, indent=4)) return params
def test(vars=None): """ Check user node mapping :param vars: `addnode` as key - Node ID of node to be mapped to user,\n defaults to `None` :type vars: dict """ node_id = vars['addnode'] if node_id is None or not vars.get('addnode'): print('Error: The following arguments are required: --addnode\n' 'Check usage: rainmaker.py [-h]\n') sys.exit(0) node_object = node.Node(node_id, session.Session()) request_id, secret_key = add_node(node_object) config = configmanager.Config() user_id = config.get_user_id() print('Use following command on node simulator or ' 'node CLI to confirm user node mapping:') print(" add-user " + user_id + " " + secret_key) print("---------------------------------------------------") print("RequestId for user node mapping request : " + request_id) check_status(node_object, request_id) return
def remove_node(vars=None): """ Removes the user node mapping. :param vars: `nodeid` as key - Node ID for the node, defaults to `None` :type vars: dict | None :raises NetworkError: If there is a network connection issue during HTTP request for removing node :raises Exception: If there is an HTTP issue while removing node or JSON format issue in HTTP response :return: None on Success :rtype: None """ log.info('Removing user node mapping for node ' + vars['nodeid']) try: n = node.Node(vars['nodeid'], session.Session()) params = n.remove_user_node_mapping() except Exception as remove_node_err: log.error(remove_node_err) else: log.debug('Removed the user node mapping successfully.') print('Removed node ' + vars['nodeid'] + ' successfully.') return
def start_claim_process(mac_addr, node_platform, private_key): log.info("Creating session") curr_session = session.Session() header = curr_session.request_header try: # Set claim initiate data claim_init_data = set_claim_initiate_data(mac_addr, node_platform) # Perform claim initiate request claim_init_resp = claim_initiate(claim_init_data, header) # Set claim verify data claim_verify_data, node_info = set_claim_verify_data( claim_init_resp, private_key) # Perform claim verify request claim_verify_resp = claim_verify(claim_verify_data, header) # Get certificate from claim verify response node_cert = json.loads(claim_verify_resp.text)['certificate'] print("Claim certificate received") log.info("Claim certificate received") return node_info, node_cert except requests.exceptions.SSLError: raise SSLError except requests.ConnectionError: log.error("Please check the Internet connection.") exit(0)
def set_params(vars=None): """ Set parameters of the node. :param vars: `nodeid` as key - Node ID for the node,\n `data` as key - JSON data containing parameters to be set `or`\n `filepath` as key - Path of the JSON file containing parameters to be set,\n defaults to `None` :type vars: dict | None :raises Exception: If there is an HTTP issue while setting params or JSON format issue in HTTP response :return: None on Success :rtype: None """ log.info('Setting params of the node with nodeid : ' + vars['nodeid']) data = vars['data'] filepath = vars['filepath'] if data is not None: log.debug('Setting node parameters using JSON data.') # Trimming white spaces except the ones between two strings data = re.sub( r"(?<![a-z]|[A-Z])\s(?![a-z]|[A-Z])|\ (?<=[a-z]|[A-Z])\s(?![a-z]|[A-Z])|\ (?<![a-z]|[A-Z])\s(?=[a-z]|[A-Z])", "", data) try: log.debug('JSON data : ' + data) data = json.loads(data) except Exception: raise InvalidJSONError return elif filepath is not None: log.debug('Setting node parameters using JSON file.') file = Path(filepath) if not file.exists(): log.error('File %s does not exist!' % file.name) return with open(file) as fh: try: data = json.load(fh) log.debug('JSON filename :' + file.name) except Exception: raise InvalidJSONError return try: n = node.Node(vars['nodeid'], session.Session()) status = n.set_node_params(data) except Exception as set_params_err: log.error(set_params_err) else: print('Node state updated successfully.') return
def remove_shared_nodes(vars=None): """ Remove shared nodes :param vars: `nodes` as key - Node Id for the node :type vars: dict :param vars: `email` as key - Email address of the user :type vars: dict :raises Exception: If there is an issue while removing shared nodes :return: None on Success :rtype: None """ try: log.debug("Removing shared nodes") # Remove any spaces if exist node_ids = vars['nodes'].strip() # Check user input format ret_status = _check_user_input(node_ids) # Get email-id email_id = vars['email'] log.debug("Email-id set to: {}".format(email_id)) # Create API data dictionary api_data = {} api_data['nodes'] = node_ids api_data['email'] = email_id n = node.Node(None, session.Session()) log.debug("API data set to: {}".format(api_data)) # API call to remove the shared nodes node_json_resp = n.remove_shared_nodes(api_data) log.debug("Remove shared nodes response: {}".format(node_json_resp)) except Exception as get_node_status_err: log.error(get_node_status_err) else: try: log.debug("Displaying status") # Display result if not isinstance(node_json_resp, dict): print(node_json_resp) else: _display_status(node_json_resp) except AttributeError as err: log.debug("Error: {}".format(err)) _display_json(node_json_resp) log.debug("Removing shared nodes successful") return
def logout(vars=None): """ Logout of the current session. :raises Exception: If there is any issue in logout for user :return: None on Success and Failure :rtype: None """ log.info('Logging out current logged-in user') # Removing the creds stored locally log.debug("Removing creds stored locally, invalidating token") config = configmanager.Config() # Get email-id of current logged-in user email_id = config.get_token_attribute('email') log.info('Logging out user: {}'.format(email_id)) # Ask user for ending current session # Get user input input_resp = config.get_input_to_end_session(email_id) if not input_resp: return # Call Logout API try: curr_session = session.Session() status_resp = curr_session.logout() log.debug("Logout API successful") except Exception as logout_err: log.error(logout_err) return # Check API status in response if 'status' in status_resp and status_resp['status'] == 'failure': print("Logout from ESP RainMaker Failed. Exiting.") print("[{}]:{}".format(status_resp['error_code'], status_resp['description'])) return # Remove current login creds ret_val = config.remove_curr_login_creds() if ret_val is None: print("Logout from ESP RainMaker Failed. Exiting.") return # Logout is successful print("Logged out from ESP RainMaker") log.debug('Logout Successful') log.debug("Local creds removed successfully") return
def login(self, password=None): """ User login to the ESP Rainmaker. :param password: Password of user, defaults to `None` :type password: str :raises NetworkError: If there is a network connection issue during login :raises Exception: If there is an HTTP issue during login or JSON format issue in HTTP response :raises AuthenticationError: If login failed with the given parameters :return: :class:`rmaker_lib.session.Session` on Success :rtype: object """ log.info("User login with username : "******"Login request url : " + login_url) response = requests.post(url=login_url, data=json.dumps(login_info), headers=self.__request_header, verify=configmanager.CERT_FILE) log.debug("Login response : " + response.text) response.raise_for_status() except requests.exceptions.SSLError: raise SSLError except requests.exceptions.ConnectionError: raise NetworkError except Exception: raise Exception(response.text) try: result = json.loads(response.text) except Exception as json_decode_err: raise json_decode_err if 'status' in result and result['status'] == 'success': log.info("Login successful.") config_data = {} config_data['idtoken'] = result['idtoken'] config_data['refreshtoken'] = result['refreshtoken'] config_data['accesstoken'] = result['accesstoken'] configmanager.Config().set_config(config_data) return session.Session() raise AuthenticationError
def list_sharing_details(node_id=None, primary_user=False, request_id=None, list_requests=False): """ List sharing details of all nodes associated with user or List pending requests :param vars: `node_id` as key - Node Id of the node(s) (if not provided, is set to all nodes associated with user) :type vars: str :param vars: `primary_user` as key - User is primary or secondary (if provided, user is primary user) :type vars: bool :param vars: `request_id` as key - Id of sharing request :type vars: str :param vars: `list_requests` as key - If True, list pending requests If False, list sharing details of nodes :type vars: bool :raises Exception: If there is an issue while listing details :return: API response :rtype: dict """ node_obj = node.Node(node_id, session.Session()) log.debug("Node id received from user: {}".format(node_id)) # Set data for listing pending requests if list_requests: # Create API query params api_params = {} if request_id: api_params['id'] = request_id api_params['primary_user'] = "******".format(primary_user) node_json_resp = node_obj.get_shared_nodes_request(api_params) log.debug("List sharing request response: {}".format(node_json_resp)) else: # Get sharing details of all nodes associated with user # API node_json_resp = node_obj.get_sharing_details_of_nodes() log.debug("Get shared nodes response: {}".format(node_json_resp)) return node_json_resp
def remove_sharing(nodes=None, user=None, request_id=None): """ Remove user from shared nodes or Remove sharing request :param vars: `nodes` as key - Node Id for the node :type vars: str :param vars: `user` as key - User name :type vars: str :param vars: `request_id` as key - Id of sharing request :type vars: str :raises Exception: If there is an issue while remove operation :return: API response :rtype: dict """ print('\nPlease make sure current (logged-in) user is Primary user\n') node_json_resp = None node_obj = node.Node(None, session.Session()) if request_id: # API call to remove the shared nodes request node_json_resp = node_obj.remove_shared_nodes_request(request_id) log.debug("Remove sharing request response: {}".format(node_json_resp)) else: # Remove any spaces if exist node_ids = nodes.strip() # Check user input format ret_status = _check_user_input(node_ids) # Create API query params dictionary api_params = {} api_params['nodes'] = node_ids api_params['user_name'] = user log.debug("API data set to: {}".format(api_params)) # API call to remove the shared nodes node_json_resp = node_obj.remove_user_from_shared_nodes(api_params) log.debug("Remove user from shared nodes response: {}".format(node_json_resp)) return node_json_resp
def get_node_status(vars=None): """ Shows the online/offline status of the node. :param vars: `nodeid` as key - Node ID for the node, defaults to `None` :type vars: dict | None :raises Exception: If there is an HTTP issue while getting node status :return: None on Success :rtype: None """ try: n = node.Node(vars['nodeid'], session.Session()) node_status = n.get_node_status() except Exception as get_node_status_err: log.error(get_node_status_err) else: print(json.dumps(node_status, indent=4)) return
def get_user_details(vars=None): """ Get details of current logged-in user """ try: # Get user details log.debug('Getting details of current logged-in user') curr_session = session.Session() user_info = curr_session.get_user_details() log.debug("User details received") except Exception as err: log.error(err) else: # Print API response output for key, val in user_info.items(): if key == "user_name": key = key + " (email)" title = key.replace("_", " ").title() print("{}: {}".format(title, val)) return
def sharing_request_op(accept_request=False, request_id=None): """ Accept or decline sharing request :param vars: `accept_request` as key If true, accept sharing request If false, decline sharing request :type vars: bool :param vars: `request_id` as key - Id of sharing request :type vars: str :raises Exception: If there is an issue accepting or declining request :return: API response :rtype: dict """ log.debug("Accept sharing request") print('\nPlease make sure current (logged-in) user is Secondary user\n') if accept_request: print("Accepting request") else: print("Declining request") # Create API data dictionary api_data = {} api_data['accept'] = accept_request api_data['request_id'] = request_id node_obj = node.Node(None, session.Session()) log.debug("API data set: {}".format(api_data)) # API to accept or decline node sharing request node_json_resp = node_obj.request_op(api_data) log.debug("Sharing request API response: {}".format(node_json_resp)) return node_json_resp
def add_user_to_share_nodes(nodes=None, user=None): """ Add user to share nodes :param vars: `nodes` as key - Node Id of the node(s) :type vars: str :param vars: `user` as key - User name :type vars: str :raises Exception: If there is an issue while adding user to share nodes :return: API response :rtype: dict """ log.debug("Adding user to share nodes") # Remove any spaces if exist nodes = nodes.strip() # Check user input format ret_status = _check_user_input(nodes) # Create list from node ids string node_id_list = _set_node_ids_list(nodes) log.debug("Node ids list: {}".format(node_id_list)) log.debug("User name is set: {}".format(user)) # Create API input info api_input = {} api_input['nodes'] = node_id_list api_input['user_name'] = user log.debug("API data set: {}".format(api_input)) # API node_obj = node.Node(None, session.Session()) node_json_resp = node_obj.add_user_for_sharing(api_input) log.debug("Set shared nodes response: {}".format(node_json_resp)) return node_json_resp
def list_shared_nodes(vars=None): """ List shared nodes :param vars: `node` as key - Node Id for the node (if provided) :type vars: dict :raises Exception: If there is an issue while getting shared nodes :return: None on Success :rtype: None """ try: log.debug("Get shared nodes") n = node.Node(vars['node'], session.Session()) log.debug("Node id received from user: {}".format(vars['node'])) # API node_json_resp = n.get_shared_nodes() log.debug("Get shared nodes response: {}".format(node_json_resp)) except Exception as get_node_status_err: log.error(get_node_status_err) else: try: # Display result log.debug("Displaying status") _display_status(node_json_resp) except AttributeError as err: log.debug("Error: {}".format(err)) _display_json(node_json_resp) log.debug("Get shared nodes successful") return
def get_nodes(vars=None): """ List all nodes associated with the user. :param vars: No Parameters passed, defaults to `None` :type vars: dict | None :raises Exception: If there is an HTTP issue while getting nodes :return: None on Success :rtype: None """ try: s = session.Session() nodes = s.get_nodes() except Exception as get_nodes_err: log.error(get_nodes_err) else: if len(nodes.keys()) == 0: print('User is not associated with any nodes.') return for key in nodes.keys(): print(nodes[key].get_nodeid()) return
def ota_upgrade(vars=None): """ Upload OTA Firmware Image and Set image url returned in response as node params """ try: node_id = vars['nodeid'] img_file_path = vars['otaimagepath'] if os.path.isabs(img_file_path) is False: img_file_path = os.path.join(os.getcwd(), img_file_path) img_name = img_file_path.split('/')[-1].split('.bin')[0] with open(img_file_path, 'rb') as f: fw_img_bytes = f.read() base64_fw_img = base64.b64encode(fw_img_bytes).decode('ascii') retries = MAX_HTTP_CONNECTION_RETRIES node_object = None status = None response = None service_name = None service_obj = None service_config = None node_params = None param_url_to_set = None curr_session = None while retries > 0: try: # If session is expired then to initialise the new session # internet connection is required. if not curr_session: curr_session = session.Session() if not node_object: node_object = node.Node(node_id, curr_session) log.info("Creating service object...") if not service_obj: service_obj = service.Service() log.info("Checking service " + service.OTA_SERVICE_TYPE + " in node config...") print("Checking " + service.OTA_SERVICE_TYPE + " in node config...") if not service_config and not service_name: service_config, service_name = service_obj.verify_service_exists(node_object, service.OTA_SERVICE_TYPE) if not service_config: log.error(service.OTA_SERVICE_TYPE + " not found.") break log.info("Checking service " + service.OTA_SERVICE_TYPE + " in config...Success") log.debug("Service config received: " + str(service_config) + " Service name received: " + str(service_name)) print("Uploading OTA Firmware Image...This may take time...") log.info("Uploading OTA Firmware Image...This may take time...") if not status and not response: # Upload OTA Firwmare Image status, response = service_obj.upload_ota_image(node_object, img_name, base64_fw_img) if status: break except SSLError: log.error(SSLError()) break except (NetworkError, RequestTimeoutError) as conn_err: print(conn_err) log.warn(conn_err) except Exception as node_init_err: log.error(node_init_err) break time.sleep(5) retries -= 1 if retries: print("Retries left:", retries) log.info("Retries left: " + str(retries)) if node_object is None: log.error('Initialising new session...Failed\n') return if not status or not 'success' in status: print("\n") log.error("OTA Upgrade...Failed") log.debug('OTA Upgrade...Failed ' 'status: ' + str(status) + ' response: ' + str(response)) return log.info('Upload OTA Firmware Image Request...Success') log.debug("Upload OTA Firmware Image Request - Status: " + json.dumps(status) + " Response: " + json.dumps(response)) retries = MAX_HTTP_CONNECTION_RETRIES ota_start_status = None node_params = None service_read_params = None service_write_params = None ota_status = None while retries > 0: try: if 'image_url' in response: param_url_to_set = response["image_url"] if not service_read_params and not service_write_params: log.info("Getting service params from node config") service_read_params, service_write_params = service_obj.get_service_params(service_config) log.debug("Service params received with read properties: " + str(service_read_params) + " Service params received with write properties: " + str(service_write_params)) log.info("Getting node params...") if not node_params: node_params = node_object.get_node_params() log.debug("Node params received: " + json.dumps(node_params)) print("Setting the OTA URL parameter...") if not ota_start_status: ota_start_status = service_obj.start_ota(node_object, node_params, service_name, service_write_params, param_url_to_set) log.debug("OTA status received: " + str(ota_start_status)) if not ota_start_status: log.error("Failed to start OTA service...Exiting...") break print("Getting OTA Status...") if not ota_status: ota_status = service_obj.check_ota_status(node_object, service_name, service_read_params) break except SSLError: log.error(SSLError()) break except (NetworkError, RequestTimeoutError) as conn_err: print(conn_err) log.warn(conn_err) except Exception as node_init_err: log.error(node_init_err) break time.sleep(5) retries -= 1 if retries: print("Retries left:", retries) log.info("Retries left: " + str(retries)) if ota_status in [None, False]: log.error("OTA Upgrade...Failed") log.debug('OTA Upgrade...Failed ' 'ota_status: ' + str(ota_status)) except KeyError as key_err: log.error("Key Error: " + str(key_err)) except Exception as ota_err: log.error(ota_err) return
def provision(vars=None): """ Provisioning of the node. :raises NetworkError: If there is a network connection issue during provisioning :raises Exception: If there is an HTTP issue during provisioning :param vars: `pop` - Proof of Possession of the node, defaults to `None` :type vars: dict :return: None on Success and Failure :rtype: None """ log.info('Provisioning the node.') secret_key = str(uuid.uuid4()) pop = vars['pop'] try: config = configmanager.Config() userid = config.get_user_id() log.debug('User session is initialized for the user ' + userid) except Exception as get_user_id_err: log.error(get_user_id_err) sys.exit(1) try: input('Please connect to the wifi PROV_XXXXXX and ' 'Press Enter to continue...') except Exception: print("Exiting...") sys.exit(0) node_id = provision_device(TRANSPORT_MODE_SOFTAP, pop, userid, secret_key) if node_id is None: log.error(PROVISION_FAILURE_MSG) return log.debug('Node ' + node_id + ' provisioned successfully.') print('------------------------------------------') input('Please ensure host machine is connected to internet and ' 'Press Enter to continue...') retries = MAX_HTTP_CONNECTION_RETRIES node_object = None while retries > 0: try: # If session is expired then to initialise the new session # internet connection is required. node_object = node.Node(node_id, session.Session()) except SSLError: log.error(SSLError()) break except (NetworkError, RequestTimeoutError) as conn_err: print(conn_err) log.warn(conn_err) except Exception as node_init_err: log.error(node_init_err) break else: break time.sleep(5) retries -= 1 if retries: print("Retries left:", retries) log.info("Retries left: " + str(retries)) if node_object is None: log.error('Initialising new session...Failed\n' + '\n' + PROVISION_FAILURE_MSG) return print('\nAdding User-Node association') log.info("Adding User-Node association") retries = MAX_HTTP_CONNECTION_RETRIES request_id = None log.info('Sending User-Node Association Request...') while retries > 0: print('Sending User-Node Association Request...') try: request_id = node_object.add_user_node_mapping(secret_key) except SSLError: log.error(SSLError()) break except (NetworkError, RequestTimeoutError) as conn_err: print(conn_err) log.warn(conn_err) except Exception as mapping_err: print(mapping_err) log.warn(mapping_err) break else: if request_id is not None: log.debug('User-Node mapping added successfully ' 'with request_id ' + request_id) break retries -= 1 if retries: print("Retries left:", retries) log.info("Retries left: " + str(retries)) time.sleep(5) if request_id is None: log.error('User-Node Association Request...Failed\n' + '\n' + PROVISION_FAILURE_MSG) return print('User-Node Association Request...Success') log.info('User-Node Association Request...Success') retries = MAX_HTTP_CONNECTION_RETRIES status = None log.info('Checking User-Node Association Status...') while retries > 0: print('Checking User-Node Association Status...') try: status = node_object.get_mapping_status(request_id) except SSLError: log.error(SSLError()) break except (NetworkError, RequestTimeoutError) as conn_err: print(conn_err) log.warn(conn_err) status = None except Exception as mapping_status_err: print(mapping_status_err) log.warn(mapping_status_err) break else: if status == 'requested': print('User-Node Association Status - Requested' '\n') log.debug('User-Node Association Status - Requested' '\n') elif status == 'confirmed': print('User-Node Association Status - Confirmed' '\nProvisioning was Successful.') log.debug('User-Node Association Status - Confirmed' '\nProvisioning was Successful.') break elif status == 'timedout': print('User-Node Association Status - Timedout') log.debug('User-Node Association Status - Timedout') break elif status == 'discarded': print('User-Node Association Status - Discarded') log.debug('User-Node Association Status - Discarded') break else: log.debug('User-Node Association Status - ' + status) break if status not in ["requested"]: retries -= 1 if retries: print("Retries left:", retries) log.info("Retries left: " + str(retries)) time.sleep(5) if status not in ["confirmed"]: log.error('Checking User-Node Association Status...Failed.\n' '\nCould not confirm User-Node Association Status. ' '\nPlease use cli command ' '`python3 rainmaker.py getnodes` to confirm.') return return
def claim(port): """ Claim the node connected to the given serial port (Get cloud credentials) :param port: Serial Port :type port: str :raises Exception: If there is an HTTP issue while claiming SSLError: If there is an issue in SSL certificate validation ConnectionError: If there is network connection issue :return: None on Success :rtype: None """ try: node_id = None node_info = None hmac_challenge = None claim_verify_data = None claim_initiate_url = CLAIM_INITIATE_URL claim_verify_url = CLAIM_VERIFY_URL private_key = None curr_claim_data = None user_whitelist_err_msg = ('user is not allowed to claim esp32 device.' ' please contact administrator') config = configmanager.Config() userid = config.get_user_id() creds_dir = Path( path.expanduser( str(Path(path.expanduser(configmanager.HOME_DIRECTORY))) + '/' + str(Path(path.expanduser(configmanager.CONFIG_DIRECTORY))) + '/claim_data/' + userid)) if not creds_dir.exists(): os.makedirs(path.expanduser(creds_dir)) log.debug("Creating new directory " + str(creds_dir)) print("\nClaiming process started. This may take time.") log.info("Claiming process started. This may take time.") node_platform, mac_addr = get_node_platform_and_mac(esptool, port) print("Node platform detected is: ", node_platform) print("MAC address is: ", mac_addr) log.debug("MAC address received: " + mac_addr) log.debug("Node platform detected is: " + node_platform) log.info("Creating session") curr_session = session.Session() header = curr_session.request_header start = time.time() mac_dir = Path(path.expanduser(str(creds_dir) + '/' + mac_addr)) if not mac_dir.exists(): os.makedirs(path.expanduser(mac_dir)) log.debug("Creating new directory " + str(mac_dir)) output_bin_filename = mac_addr + '.bin' mac_dir_path = str(mac_dir) + '/' # Set values dest_filedir = mac_dir_path # Set csv file data node_info_csv = [ 'key,type,encoding,value', 'rmaker_creds,namespace,,', 'node_id,file,binary,' + dest_filedir + 'node.info', 'mqtt_host,file,binary,' + dest_filedir + 'endpoint.info', 'client_cert,file,binary,' + dest_filedir + 'node.crt', 'client_key,file,binary,' + dest_filedir + 'node.key' ] # Generate nvs args to be sent to NVS Partition Utility nvs_args = SimpleNamespace(input=dest_filedir + 'node_info.csv', output=output_bin_filename, size='0x6000', outdir=dest_filedir, version=2) # Set config mac addr path mac_addr_config_path = str(Path(path.expanduser( configmanager.CONFIG_DIRECTORY))) + '/claim_data/' +\ userid +\ '/' +\ mac_addr +\ '/' + output_bin_filename # Check if claim data for node exists in CONFIG directory log.debug("Checking if claim data for node exists in directory: " + configmanager.HOME_DIRECTORY + configmanager.CONFIG_DIRECTORY) curr_claim_data = configmanager.Config().get_binary_config( config_file=mac_addr_config_path) if curr_claim_data: print("\nClaiming data already exists at location: " + dest_filedir) log.debug("Claiming data already exists at location: " + dest_filedir) log.info("Using existing claiming data") print("Using existing claiming data") # Flashing existing binary onto node print("\nFlashing existing binary onto node\n") log.info("Flashing existing binary onto node") flash_bin_onto_node(port, esptool, dest_filedir + output_bin_filename) log.info("Binary flashed onto node") return # Generate Key log.info("Generate RSA key") private_key = rsa.generate_private_key(public_exponent=65537, key_size=2048, backend=default_backend()) log.info("RSA Private Key generated") # Set Claim initiate request data claim_initiate_data = {"mac_addr": mac_addr, "platform": node_platform} claim_init_enc_data = str(claim_initiate_data).replace("'", '"') print("Claim initiate started") # Sign the CSR using the CA try: # Claim Initiate Request log.info("Claim initiate started. Sending claim/initiate POST\ request") log.debug("Claim Initiate POST Request: url: " + claim_initiate_url + "data: " + str(claim_init_enc_data) + "headers: " + str(header) + "verify: " + CERT_FILE) claim_initiate_response = requests.post(url=claim_initiate_url, data=claim_init_enc_data, headers=header, verify=CERT_FILE) if claim_initiate_response.status_code != 200: log.error("Claim initiate failed.\n" + claim_initiate_response.text) exit(0) print("Claim initiate done") log.debug("Claim Initiate POST Response: status code: " + str(claim_initiate_response.status_code) + " and response text: " + claim_initiate_response.text) log.info("Claim initiate done") # Get data from response depending on node_platform if node_platform == "esp32": # Generate CSR with common_name=node_id received in response node_id = str( json.loads(claim_initiate_response.text)['node_id']) print("Generating CSR") log.info("Generating CSR") csr = gen_host_csr(private_key, common_name=node_id) if not csr: raise Exception("CSR Not Generated. Claiming Failed") log.info("CSR generated") claim_verify_data = {"csr": csr} # Save node id as node info to use while saving claim data # in csv file node_info = node_id else: auth_id = str( json.loads(claim_initiate_response.text)['auth_id']) hmac_challenge = str( json.loads(claim_initiate_response.text)['challenge']) print("Generating CSR") log.info("Generating CSR") csr = gen_host_csr(private_key, common_name=mac_addr) if not csr: raise Exception("CSR Not Generated. Claiming Failed") log.info("CSR generated") log.info("Getting secret key from device") secret_key = get_secret_key(port, esptool) log.info("Getting secret key from device") log.info("Generating hmac challenge response") hmac_challenge_response = gen_hmac_challenge_resp( secret_key, hmac_challenge) hmac_challenge_response = hmac_challenge_response.strip('\n') log.debug("Secret Key generated: " + secret_key) log.debug("HMAC Challenge Response: " + hmac_challenge_response) claim_verify_data = { "auth_id": auth_id, "challenge_response": hmac_challenge_response, "csr": csr } # Save node id as node info to use while saving claim data # in csv file node_info = mac_addr claim_verify_enc_data = str(claim_verify_data).replace("'", '"') log.debug("Claim Verify POST Request: url: " + claim_verify_url + "data: " + str(claim_verify_enc_data) + "headers: " + str(header) + "verify: " + CERT_FILE) claim_verify_response = requests.post(url=claim_verify_url, data=claim_verify_enc_data, headers=header, verify=CERT_FILE) if claim_verify_response.status_code != 200: claim_verify_response_json = json.loads( claim_verify_response.text.lower()) if (claim_verify_response_json["description"] in user_whitelist_err_msg): log.error('Claim verification failed.\n' + claim_verify_response.text) print( '\nYour account isn\'t whitelisted for ESP32.' ' Please send your registered email address to' ' [email protected] for whitelisting') else: log.error('Claim verification failed.\n' + claim_verify_response.text) exit(0) print("Claim verify done") log.debug("Claim Verify POST Response: status code: " + str(claim_verify_response.status_code) + " and response text: " + claim_verify_response.text) log.info("Claim verify done") node_cert = json.loads(claim_verify_response.text)['certificate'] print("Claim certificate received") log.info("Claim certificate received") except requests.exceptions.SSLError: raise SSLError except requests.ConnectionError: log.error("Please check the Internet connection.") exit(0) # Set node claim data sys.stdout = StringIO() log.info("Getting MQTT Host") endpointinfo = node.get_mqtt_host(None) log.debug("Endpoint info received: " + endpointinfo) sys.stdout = sys.__stdout__ # Extract private key in bytes from private key object generated log.info("Extracting private key in bytes") node_private_key = private_key.private_bytes( encoding=serialization.Encoding.PEM, format=serialization.PrivateFormat.TraditionalOpenSSL, encryption_algorithm=serialization.NoEncryption()) # Create files of each claim data info print("\nSaving claiming data info at location: ", dest_filedir) log.debug("Saving claiming data info at location: " + dest_filedir) create_files_of_claim_info(dest_filedir, node_info, node_private_key, node_cert, endpointinfo, node_info_csv) # Run NVS Partition Utility to create binary of node info data print("\nGenerating NVS Partition Binary from claiming data: " + dest_filedir + output_bin_filename) log.debug("Generating NVS Partition Binary from claiming data: " + dest_filedir + output_bin_filename) nvs_partition_gen.generate(nvs_args) print("\nFlashing onto node\n") log.info("Flashing binary onto node") flash_bin_onto_node(port, esptool, dest_filedir + output_bin_filename) print("Claiming done") log.info("Claiming done") print("Time(s):" + str(time.time() - start)) except Exception as err: log.error(err) sys.exit(err)
def provision(vars=None): """ Provisioning of the node. :raises NetworkError: If there is a network connection issue during provisioning :raises Exception: If there is an HTTP issue during provisioning :param vars: `pop` - Proof of Possession of the node, defaults to `None` :type vars: dict :return: None on Success and Failure :rtype: None """ try: from rmaker_tools.rmaker_prov.esp_rainmaker_prov\ import provision_device except ImportError as err: import google.protobuf if version.parse(google.protobuf.__version__)\ < version.parse(MINIMUM_PROTOBUF_VERSION): log.warn('Package protobuf does not satisfy\ the minimum required version.\n' 'Minimum required version is ' + MINIMUM_PROTOBUF_VERSION) else: log.error('Provisioning failed due to import error.', err) raise err log.info('Provisioning the node.') secret_key = str(uuid.uuid4()) pop = vars['pop'] try: config = configmanager.Config() userid = config.get_user_id() log.debug('User session is initialized for the user ' + userid) except Exception as get_user_id_err: log.error(get_user_id_err) sys.exit(1) try: input('Please connect to the wifi PROV_XXXXXX and ' 'Press Enter to continue...') except Exception: print("Exiting...") sys.exit(0) node_id = provision_device(TRANSPORT_MODE_SOFTAP, pop, userid, secret_key) if node_id is None: print(PROVISION_FAILURE_MSG) return log.debug('Node ' + node_id + ' provisioned successfully.') print('------------------------------------------') input('Please ensure host machine is connected to internet and' 'Press Enter to continue...') print('Adding User-Node association...') retries = MAX_HTTP_CONNECTION_RETRIES node_object = None while retries > 0: try: # If session is expired then to initialise the new session # internet connection is required. node_object = node.Node(node_id, session.Session()) except SSLError: log.error(SSLError()) print(PROVISION_FAILURE_MSG) return except NetworkError: time.sleep(5) log.warn("Session is expired. Initialising new session.") pass except Exception as node_init_err: log.error(node_init_err) print(PROVISION_FAILURE_MSG) return else: break retries -= 1 if node_object is None: print('Please check the internet connectivity.') print(PROVISION_FAILURE_MSG) return retries = MAX_HTTP_CONNECTION_RETRIES request_id = None while retries > 0: try: log.debug('Adding user-node association.') request_id = node_object.add_user_node_mapping(secret_key) except SSLError: log.error(SSLError()) print(PROVISION_FAILURE_MSG) return except Exception as user_node_mapping_err: print('Sending User-Node association request to ' 'ESP RainMaker Cloud - Failed\nRetrying...') log.warn(user_node_mapping_err) pass else: if request_id is not None: log.debug('User-node mapping added successfully' 'with request_id' + request_id) break time.sleep(5) retries -= 1 if request_id is None: print('Sending User-Node association request to' 'ESP RainMaker Cloud - Failed') print(PROVISION_FAILURE_MSG) return print('Sending User-Node association request to' 'ESP RainMaker Cloud - Successful') status = None while True: log.debug('Checking user-node association status.') try: status = node_object.get_mapping_status(request_id) except SSLError: log.error(SSLError()) print(PROVISION_FAILURE_MSG) return except Exception as mapping_status_err: log.warn(mapping_status_err) pass else: log.debug('User-node association status ' + status) if status == 'requested': print('Checking User Node association status -' 'Requested\nRetrying...') elif status == 'confirmed': print('Checking User Node association status - Confirmed') print('Provisioning was Successful.') return elif status == 'timedout': print('Checking User Node association status - Timeout') print(PROVISION_FAILURE_MSG) return elif status == 'discarded': print('Checking User Node association status - Discarded') print(PROVISION_FAILURE_MSG) return time.sleep(5) if status is None: print(PROVISION_FAILURE_MSG) print('Checking User Node association status failed. ' 'Please check the internet connectivity.') return return