def setup(self, dest=''): ''' CLI Setup ''' try: # Set Parent Parser log.debug("Setting up argparser") self.parser = argparse.ArgumentParser( description=self.description, formatter_class=self.formatter_class) log.debug("Parent parser set") # Add parent subparser parent_subparser = self._add_subparser( self.parser, title='Commands', help_text='usage: rainmaker_admin_cli.py {command} -h for ' 'additional help', dest=dest) return parent_subparser except KeyError as key_err: log.error(KeyError(str(key_err))) print(traceback.format_exc()) except AttributeError as attr_err: log.error(AttributeError(str(attr_err))) print(traceback.format_exc()) except Exception as err: log.error( CLIError('ERROR: Error occurred during ' 'CLI Setup. {}'.format(err))) print(traceback.format_exc()) return
def configure_server(vars=None): ''' Set Server Config :param vars: `endpoint` as key - Endpoint of server to use :type vars: str :raises Exception: If there is any exception while configuring server KeyboardInterrupt: If there is a keyboard interrupt by user :return: None on Failure :rtype: None ''' try: log.debug("Configure server") ret_status = _cli_arg_check(vars['endpoint'], '--endpoint <endpoint>') if not ret_status: return config = configmanager.Config(config="server") ret_status = config.set_server_config(vars['endpoint']) if not ret_status: log.error("Failed to save server config") return else: log.info("Saved new server config") log.info('You can now run: \npython rainmaker_admin_cli.py account ' 'login -h (Login)') except KeyboardInterrupt: log.error("\nServer config not set") except Exception as e: log.error("Error: {}".format(e))
def save_config(self, data): ''' Save login config data to file :param data: Login data to be set :type data: dict ''' try: log.debug("Saving login config data") if not os.path.isdir(CONFIG_DIRECTORY): log.info('Config directory does not exist, ' 'creating new directory : {}'.format( CONFIG_DIRECTORY)) os.makedirs(CONFIG_DIRECTORY) login_cfg_data = self._set_login_config_data(data) if not login_cfg_data: return False, False with open(self.config_file, 'w+', encoding='utf-8') as cfg_file: cfg_file.write(str(json.dumps(login_cfg_data))) return True, self.config_file except Exception as save_config_err: log.error(save_config_err) return False, False
def generate_cacert(cacert_info, ca_private_key): ''' Generate CA Certificate :param cacert_info: CA certificate info :type cacert_info: dict :param ca_private_key: CA Private Key :type ca_private_key: RSA Private Key ''' try: log.info("\nGenerating CA Certificate") ca_public_key = ca_private_key.public_key() subj_name_list = _create_subj_name_list(cacert_info) if not subj_name_list: return False issuer_name = [ x509.NameAttribute(NameOID.COMMON_NAME, cacert_info['common_name']) ] ca_cert = _generate_cert(subject_name=subj_name_list, issuer_name=issuer_name, public_key=ca_public_key, ca_key=ca_private_key, ca=True) if not ca_cert: return False return ca_cert except Exception as err: log.debug("Error: {} . Cannot create CA Certificate".format(err))
def _get_cacert_user_input(cfg, cfg_menu): ''' Get CA Certficate Data from User :param cfg: CA Certificate config :type cfg: dict :param cfg_menu: CA Certificate config menu :type cfg_menu: dict :return: Config data :rtype: dict ''' # Get CA Certificate data input from user log.debug("Getting CA certificate config data from User") for (cfg_key, _) in iteritems(cfg): log.debug("Config Key: {}".format(cfg_key)) while True: input_data = input(cfg_menu[cfg_key]) log.debug("Input received from user: {}".format(input_data)) if input_data: if cfg_key == "country_name" and not len(input_data) == 2: print("Country name must be a 2 character country code") continue cfg[cfg_key] = str(input_data) log.debug("Config: {} set for key: {}".format( input_data, cfg_key)) elif not input_data and cfg_key == "common_name": print("Common Name is mandatory") continue break log.debug("CA Certificate config data received from user") return cfg
def download_from_url(request_url): ''' Download file from url :param request_url: Request URL to download file from :type request_url: str ''' try: response = None while True: try: log.debug("Downloading file from url: {}".format(request_url)) response = requests.get(url=request_url) response.raise_for_status() except requests.exceptions.SSLError: raise(SSLError()) except (ConnectionError): log.error(NetworkError()) except RequestException as err: log.error(err) if response: log.info("Node ids file downloaded successfully") log.debug("Response content: {}".format(response.content)) return response.content else: log.info("Retrying...") time.sleep(5) except Exception as err: raise Exception(err)
def _extra_config_files_checks(outdir, extra_config, extra_values, file_id): log.debug("Extra config file checks") set_to_false = None if extra_config and not extra_values: log.info('ADDITIONAL_VALUES file must also be provided in config ' 'alongwith ADDITIONAL_CONFIG file.') set_to_false = False if file_id and file_id in ['node_id']: log.info('`node_id` is the default fileid. ' 'Any new fileid provided as input must be a key in the ' 'ADDITIONAL_VALUES file provided in config.') set_to_false = False if file_id and file_id not in ['node_id'] and not extra_values: log.info( 'Fileid provided must have corresponding values in ADDITIONAL_VALUES file in config. ' 'Please provide ADDITIONAL_VALUES file in config.') set_to_false = False ''' if extra_values and file_id and file_id not in ['node_id'] and not extra_config: log.info('Fileid provided must be a config key. Please provide ADDITIONAL_CONFIG file ' 'alongwith ADDITIONAL_VALUES file in config.') set_to_false = False ''' if extra_config or extra_values: log.debug("Verifying mfg files input") # Verify mfg files verify_mfg_files(outdir, extra_config, extra_values, file_id) if set_to_false is False: return False return True
def generate_csr(private_key, common_name): ''' Generate CSR :param private_key: Private Key :type private_key: RSA Private Key :param common_name: Common Name :type common_name: str ''' try: log.debug("Generate CSR") # Generate CSR on host csr_builder = x509.CertificateSigningRequestBuilder() csr_builder = csr_builder.subject_name( x509.Name([ x509.NameAttribute(NameOID.COMMON_NAME, common_name), ])) csr_builder = csr_builder.add_extension( x509.BasicConstraints(ca=False, path_length=None), critical=True, ) csr = csr_builder.sign(private_key, hashes.SHA256(), default_backend()) if not isinstance(csr, x509.CertificateSigningRequest): print('CSR Type Is Wrong, ' 'expected x509.CertificateSigningRequest') return False return csr except Exception as err: raise Exception(err)
def __init__(self, dest_config_filename, dest_values_filename, data, outdir, keygen, file_id): self.conf = dest_config_filename self.values = dest_values_filename if data: self.size = data['BINARY_SIZE'] else: self.size = data self.outdir = outdir # Set version=2, multipage blob support enabled self.version = 2 self.keygen = keygen self.inputkey = None # These must be None, there must be no input from user for these params self.keyfile = None self.input = None self.output = None self.fileid = file_id log.debug('Arguments set to send to manufacturing tool for ' 'creating NVS partiton binaries') log.debug('conf: {}, values: {}, size: {}, ' 'outdir: {}, version: {} ' 'keygen: {}, inputkey: {}, keyfile: {}, ' 'input: {}, output: {}, fileid: {}'.format( self.conf, self.values, self.size, self.outdir, self.version, self.keygen, self.inputkey, self.keyfile, self.input, self.output, self.fileid))
def _add_subparser(self, parser, cmd="", title="", help_text="", dest=""): ''' Add subparser :param parser: Parent parser to set command for :type parser: <Argparse parser> :param cmd: CLI Command :type cmd: str :param title: CLI Command title :type title: str :param help_text: CLI Command help text :type help_text: str ''' log.debug("Adding subparser for cmd: {}".format(cmd)) # Add subparser to parser if title or help_text: subparser = parser.add_subparsers(title=title, help=help_text, dest=dest) else: subparser = parser.add_subparsers(dest=dest) return subparser
def get_fileid_val(file_identifier, keys_in_config_file, keys_in_values_file, values_data_line, key_value_data, fileid_value): """ Get file identifier value """ file_id_prefix = 'node' hyphen = '-' max_filename_len = 6 file_id_found = False fileid = None for key in key_value_data: if file_identifier and not file_id_found and file_identifier in key: key_value = key[1] file_id_found = True # Default value = str(fileid_value + 1) zeros_prefix_len = max_filename_len - len(value) zero_prefix_str = '0' * zeros_prefix_len # Set fileid fileid = file_id_prefix + hyphen + zero_prefix_str + value # If fileid is provided, suffixed here if file_id_found: fileid = fileid + hyphen + key_value log.debug("File id set to: {}".format(fileid)) return fileid
def _certs_files_init(dest_filename): log.debug("In certs files init") # Open dest nodeid and certs csv file dest_csv_file = open(dest_filename, "w+") dest_csv_file = _write_header_to_dest_csv(dest_csv_file) log.debug('Node id and Cert header keys written ' 'to csv file successfully') return dest_csv_file
def __init__(self, token): """ Instantiate node_mfg with user token (session) """ self.request_header = { 'content-type': 'application/json', 'Authorization': token } log.debug("Node Mfg request header: {}".format(self.request_header))
def __init__(self, email_id): """ Instantiate user with email_id. """ self.__email = email_id self.request_header = {'content-type': 'application/json'} log.debug('Login setup - email: {} ' 'request_header: {}'.format(self.__email, self.request_header))
def __init__(self): self.description = '\nESP Rainmaker Admin CLI' self.formatter_class = argparse.RawTextHelpFormatter self.parser = None self.args = None log.debug('Initialising argparser, default parameters set: ' 'description: {} formatter_class: {} parser: {} ' 'args: {}'.format(self.description, self.formatter_class, self.parser, self.args))
def _gen_common_files_dir(outdir): ''' Generate common files dir ''' # Create output directory for all common files generated common_outdir = os.path.join(outdir, 'common') if not os.path.isdir(common_outdir): distutils.dir_util.mkpath(common_outdir) log.debug("Directory created: {}".format(common_outdir)) return common_outdir
def _init_file(common_outdir): log.debug("In init file") # Setup destination filename if not provided dest_filename = _set_filename(filename_prefix="node_certs", outdir=common_outdir, ext=".csv") if not dest_filename: return False log.debug("Dest filename set to: {}".format(dest_filename)) return dest_filename
def get_new_token(self, refresh_token): """ Get new token for User Login :param refresh_token: Refresh Token of User :type refresh_token: str """ try: backslash = '/' socket.setdefaulttimeout(10) log.debug("Extending user login session") # Set HTTP Request path = 'login' request_payload = { 'user_name': self.__email, 'refreshtoken': refresh_token } request_url = constants.HOST.rstrip(backslash) + backslash + path log.debug('Sending HTTP POST Request - request url: {} ' 'request body: {}'.format(request_url, json.dumps(request_payload))) # Send HTTP POST Request log.debug('Sending HTTP {} request - url: {} data: {} ' 'headers: {}'.format('post', request_url, json.dumps(request_payload), self.request_header)) response = requests.post(url=request_url, data=json.dumps(request_payload), headers=self.request_header, verify=configmanager.CERT_FILE, timeout=(5.0, 5.0)) response = json.loads(response.text) log.debug("Response received: {}".format(response)) # Check response if 'accesstoken' in response and 'idtoken' in response: log.debug("User session extended successfully") return response['accesstoken'], response['idtoken'] return False, False except SSLError as ssl_err: log.error(ssl_err) except NetworkError as net_err: log.error(net_err) except RequestTimeoutError as req_err: log.error(req_err) except RequestException as req_exc_err: log.error(req_exc_err) except Exception as err: raise Exception(err)
def _read_extra_values_file_header(filename): log.debug("Read extra values file header") extra_values_file = open(filename, 'r') comments_exist_in_file = False line = extra_values_file.readline().strip() # Comments are skipped while line.startswith('#'): comments_exist_in_file = True line = extra_values_file.readline().strip() log.debug("Current line: {}".format(line)) return extra_values_file, line
def verify_fileid_count(extra_values_file, fileid, count): ''' Verify count is less than or equal to number of values for fileid ''' log.debug("Verify fileid count") with open(extra_values_file, 'r') as values_file: rows = values_file.readlines() if count > (len(rows) - 1): return False return True
def _fileid_check(file_id, node_count, extra_values): log.debug("Fileid check") if file_id: # Verify fileid count ret_status = verify_fileid_count(extra_values, file_id, node_count) if not ret_status: log.error( "Count: {} provided is greater than the values for fileid: {} in file: {}" .format(node_count, file_id, extra_values)) return False return True
def verify_mfg_files(outdir, config_filename, values_filename, file_id): ''' Verify Mfg files format and data is valid ''' common_outdir = os.path.join(outdir, 'common') mfg_args = Mfg_Args(config_filename, values_filename, None, None, None, file_id) log.debug("Verifying mfg files") # Only verify files, csv is not generated here mfg_gen.generate(mfg_args) log.debug("Mfg files verified")
def _get_and_save_ca_cert_from_input(outdir, filepath): # Get CA Certificate Data from file ca_cert = get_ca_cert_from_file(filepath) if not ca_cert: return log.debug("CA Cert data recieved") # Save CA cert if given as input ret_status = save_cert(ca_cert, os.path.join(outdir, CACERT_FILENAME)) if not ret_status: return log.debug("CA Cert saved") return ca_cert
def _create_date_dir(curr_outdir): ''' Create date dir ''' log.debug("Current outdir: {}".format(curr_outdir)) date_dirname = datetime.datetime.now().strftime('%Y-%m-%d') outdir_path = os.path.join(curr_outdir, date_dirname) if not os.path.isdir(outdir_path): # Create new directory os.makedirs(outdir_path) log.debug("Directory created: {}".format(outdir_path)) # New directory created is returned or existing one is returned return outdir_path
def _get_and_save_ca_key_from_input(outdir, filepath): # Get CA Certificate Private Key from file ca_private_key = get_ca_key_from_file(filepath) if not ca_private_key: return log.debug("CA Key data recieved") # Save CA key if given as input ret_status = save_key(ca_private_key, os.path.join(outdir, CA_KEY_FILENAME)) if not ret_status: return log.debug("CA Key saved") return ca_private_key
def _set_default_function(self, parser, cmd=""): ''' Set default function for parser :param parser: Parent parser to set command for :type parser: <Argparse parser> :param cmd: CLI Command :type cmd: str ''' func = COMMANDS[cmd]["func"] func_name = getattr(rmaker_admin_cmd.cmds, func) parser.set_defaults(func=func_name) log.debug("Default function set: {}".format(func))
def _gen_prov_data(node_id_dir, node_id_dir_str, qrcode_outdir, random_hex_str, prov_type): ''' Generate Provisioning data QR code image and string ''' log.debug("Generating QR code") # Set destination filename to save QR code image (png format) qrcode_payload_str = 'qrcode' # QR code image filename str is same as the current node id dirname str qrcode_img_str = node_id_dir_str # Generate payload (qr code payload) and png (qr code image) qrcode_payload, qrcode = generate_qrcode(random_hex_str, prov_type) log.debug("QR code and payload generated") # Save qrcode payload payload_file = save_to_file(qrcode_payload, node_id_dir, filename_prefix=qrcode_payload_str, ext=".txt") if not payload_file: return log.debug("QR code payload saved to file") # Save qrcode image to file qrcode_img_file = save_to_file(qrcode, qrcode_outdir, filename_prefix=qrcode_img_str, ext=".png") if not qrcode_img_file: return log.debug("QR code image saved to file") return True
def _create_mfg_config_file(outdir): # Set csv file data config_csv_file = [ 'rmaker_creds,namespace,', 'node_id,data,binary', 'mqtt_host,file,binary', 'client_cert,file,binary', 'client_key,file,binary', 'random,file,hex2bin' ] # Check if additonal config file is given # in the .ini file ret_val = _add_extra_config_file(config_csv_file) # Update main config file if return is a success if ret_val: config_csv_file = ret_val log.debug("Final config csv file created: {}".format(config_csv_file)) log.debug("Creating manufacturing config file") dest_config_filename = os.path.join(outdir, MFG_CONFIG_FILENAME) log.debug("Config file dest: {}".format(dest_config_filename)) with open(dest_config_filename, 'w+') as info_file: for input_line in config_csv_file: info_file.write(input_line) info_file.write("\n") log.debug("Manufacturing config file created") return dest_config_filename
def get_token_attribute(self, attribute_name, token): ''' Get User token attribute :param attribute_name: Token Attribute Name :type attribute_name: str :param token: User Token :type token: str ''' log.debug("Getting token attribute") token_payload = token.split('.')[1] if len(token_payload) % 4: token_payload += '=' * (4 - len(token_payload) % 4) log.debug("Token Payload: {}".format(token_payload)) try: str_token_payload = base64.b64decode(token_payload).decode("utf-8") log.debug("Token Playload String: {}".format(str_token_payload)) attribute_value = json.loads(str_token_payload)[attribute_name] log.debug("Attribute Value: {}".format(attribute_value)) if attribute_value is None: log.error(InvalidConfigError()) return attribute_value except Exception as err: raise Exception(err)
def _gen_random_info(node_id_dir): log.debug("Generating random info") # Set destination filename to save random info str (PoP) random_info_str = 'random' # Save random str (used a PoP) into a file random_hex_str = gen_hex_str() random_str_file = save_to_file(random_hex_str, node_id_dir, filename_prefix=random_info_str, ext=".txt") if not random_str_file: return False, False log.debug("Random info generated") return random_hex_str, random_str_file