def load(self): """ Public method which acts as device configuration loader. :rtype: dict :return: A dict containing all device parameters """ self._load_cli_conf() self._load_bench_conf() self._load_device_conf() # Load the device config from the device catalog if not self.device_conf: _error( "ACS single mode : No device config found for device {0} !". format(self._device_model_name), AcsConfigException.INVALID_PARAMETER) # Override Current DUT parameters from Bench ... self._override_parameters_bench() # ... and CLI self._override_parameters_cli() LOGGER_FWK.info('Checking device model integrity...') # Asserts All Device Model is valid (Parameters) self._validate() return self.device_only_params
def create_url_shortcut(self, campaign_url): """ Create a shortcut to open given url :rtype: tuple :return: Status and output log """ try: if os.path.exists(Folders.REPORTS): output_path = os.path.join(Folders.REPORTS, self._report_name + ".html") if not os.path.isfile(output_path): LOGGER_FWK.info( "CREATE_URL_SHORTCUT: Creating url shortcut to campaign result" ) html_content = "<html>\n" html_content += "<head>\n" html_content += "<meta http-equiv=\"refresh\" content=\"0; URL=%s\">\n" % campaign_url html_content += "</head>\n" html_content += "<body></body>\n" html_content += "</html>" with open(output_path, "w") as fd: fd.write(html_content) except Exception as ex: # pylint: disable=W0703 error_msg = "CREATE_URL_SHORTCUT: Fail, " + str(ex) LOGGER_FWK.error(error_msg) return Global.FAILURE, error_msg msg = "CREATE_URL_SHORTCUT: Created link to %s" % str(campaign_url) return Global.SUCCESS, msg
def start_campaign(self, header, payload): # Build the test report link to display in the logs campaign_id = header['requestId'] user_agent = self.__print_user_agent(header) rest_api_url = "{0}{1}".format(self._api_url, '/campaigns') headers = {'content-type': 'application/json', 'User-Agent': "{}".format(user_agent)} # first check if campaign exists campaign_url = rest_api_url + '/' + campaign_id response = self.send_get_request(url=campaign_url, headers=headers) if 'errorCode' in response and '404' in response.get('errorCode'): # Not found, create it response = self.send_start_event(url=rest_api_url, payload=payload, timeout=PUSH_EVENT_TIMEOUT, headers=headers) else: # already exists, just update response = self.send_update_event(url=campaign_url, payload=payload, timeout=PUSH_EVENT_TIMEOUT, headers=headers) # Display the campaign url if the request is correctly sent if response and response.get('id') == campaign_id: self.campaign_url = "{0}/campaigns/{1}/detail".format(self._web_reporting_url, campaign_id) LOGGER_FWK.info("Meta Campaign UUID will be: {0}".format(campaign_id)) LOGGER_FWK.info("TEST_REPORT_URL: {0}".format(self.campaign_url)) return response
def zip_tcr_campaign_data(original_folder, dest_folder, folders_and_files): """ This archive contains: dut log, acs logs """ import zipfile try: acs_logfile_name = folders_and_files["acs_logfile_name"] tcr_live_reporting_logfile = folders_and_files[ "TCR_LIVE_REPORTING"] aplog_folder = folders_and_files["AP_LOGS"] bplog_folder = folders_and_files["LOGCAT_LOGS"] dbglog_folder = folders_and_files["DEBUG_LOGS"] logcat_folder = folders_and_files["BP_LOGS"] pti_folder = folders_and_files["PTI_LOGS"] serial_folder = folders_and_files["SERIAL_LOGS"] root_dut_name = folders_and_files["ROOT_DUT_NAME"] report_style_filename = folders_and_files["REPORT_STYLE_FILENAME"] filename = "{0}.zip".format(dest_folder) zip_file = zipfile.ZipFile(filename, 'w', compression=zipfile.ZIP_DEFLATED, allowZip64=True) LOGGER.info('Create TCR campaign zip file: {0}'.format(filename)) ZipFolderUtilities.add_file_to_zip( zip_file, os.path.basename("{0}.log".format(acs_logfile_name)), original_folder) ZipFolderUtilities.add_file_to_zip( zip_file, os.path.basename("{0}.xml".format(acs_logfile_name)), original_folder) if os.path.exists(tcr_live_reporting_logfile): ZipFolderUtilities.add_file_to_zip(zip_file, tcr_live_reporting_logfile, original_folder) ZipFolderUtilities.add_file_to_zip(zip_file, report_style_filename, original_folder) for f in [ f for f in get_subdirectories(original_folder) if f.startswith(root_dut_name) ]: for local_path in [ "{0}{2}{1}".format(f, folder, os.path.sep) for folder in (aplog_folder, bplog_folder, logcat_folder, dbglog_folder, pti_folder, serial_folder) ]: ZipFolderUtilities.add_folder_to_zip( zip_file, os.path.join(original_folder, local_path), local_path) zip_file.close() status = Global.SUCCESS out_file = os.path.abspath(filename) except IOError as error: LOGGER.error('Cannot create zip file: {0} - {1}'.format( filename, error)) status = Global.FAILURE out_file = "" return status, out_file
def send_cmd(self, cmd, args={}): """ Send a command to embedded server via socket :type cmd: string :param cmd: command name :type args: dict :param args: command arguments :rtype: tuple :return: the status and the result of the command """ request = json.dumps([cmd, [], args]) LOGGER_FWK.info("Sending command %s %s" % (cmd, str(args))) try: conn = self._proto_factory.create() conn.connect() conn.send(request) status, result = json.loads(conn.receive()) self._check_cmd_status(status, result) except: msg = "Failed to communicate with embedded command server" raise DeviceException(DeviceException.OPERATION_FAILED, msg) finally: conn.disconnect() if status == CommandServerApi.SRV_CMD_DEFERRED: LOGGER_FWK.debug("Command ID: %d" % result) return status, result
def create_md5sum_file(self, file_path): """ Create md5sum file of input file :param file_path: The full file path :type file_path: str :rtype: tuple :return: status and output log (The md5sum file path and hash value) """ try: md5hash = self._get_md5sum(file_path)[1] md5sum_file_name = file_path + ".md5sum" file_name = os.path.basename(file_path) md5sum_file = open(md5sum_file_name, 'w') md5sum_file.write(md5hash) md5sum_file.write(' ') md5sum_file.write(file_name) md5sum_file.write('\r\n') md5sum_file.close() except Exception as ex: # pylint: disable=W0703 error_msg = "CREATE_MD5_FILE: Fail, " + str(ex) LOGGER.error(error_msg) return Global.FAILURE, error_msg LOGGER.info("MD5SUM FILE: %s" % md5sum_file_name) return Global.SUCCESS, md5sum_file_name
def add_file_to_zip(zip_file, file_name, file_path): full_path = os.path.abspath(os.path.join(file_path, file_name)) path_inside_zip = os.path.relpath(full_path, os.path.abspath(file_path)) LOGGER.info('File added: {0} as {1}'.format(full_path, path_inside_zip)) zip_file.write(full_path, path_inside_zip)
def _get_md5sum(cls, file_path, block_size=256 * 128): """ Gets md5sum of input file :param file_path: The full file path :type file_path: str :param block_size: define block size for reading file by chunk by chunk (mandatory for huge files) :type block_size: int :rtype: tuple :return: status and output log (The md5 hash) """ try: hex_value = None md5hash = hashlib.md5() # pylint: disable=E1101 with open(file_path, 'rb') as f: for chunk in iter(lambda: f.read(block_size), b''): md5hash.update(chunk) hex_value = md5hash.hexdigest() except Exception as ex: # pylint: disable=W0703 error_msg = "GET_MD5: Fail, %s" % str(ex) LOGGER.error(error_msg) return Global.FAILURE, error_msg LOGGER.info("MD5 HASH: " + hex_value) return Global.SUCCESS, hex_value
def addFolderToZip(zip_file, folder): for root, _, files in os.walk(folder): for f in files: full_path = os.path.abspath(os.path.join(root, f)) path_inside_zip = os.path.relpath(full_path, os.path.abspath(folder)) LOGGER.info('File added: {0} as {1}'.format( full_path, path_inside_zip)) zip_file.write(full_path, path_inside_zip)
def add_folder_to_zip(zip_file, folder, path_inside_zip): for root, _, files in os.walk(folder): for f in files: full_path = os.path.abspath(os.path.join(root, f)) f_loc = full_path.rsplit(path_inside_zip, 1)[1].strip(os.path.sep) file_path = os.path.join(path_inside_zip, f_loc) LOGGER.info('File added: {0} as {1}'.format( full_path, file_path)) zip_file.write(full_path, file_path)
def _parse_bench_node(self, node): """ Parses XML `Phone` node(s) from Bench Catalog file and maps it into a python structure (dict) :return: A dict mapping XML configuration :rtype: AttributeDict :raise AcsConfigException.INVALID_BENCH_CONFIG: If a (or more) deprecated parameter(s) is/are found in a Phone node """ LOGGER_FWK.info( 'Loading optional device model parameters from CLI and/or BenchConfig ' 'for {0} ({1})...'.format(self._device_name, self._device_model_name)) buf_params = AttributeDict() # Storing value to avoid recomputing each call device_model_name = self.device_model_name if device_model_name: buf_params["Name"] = device_model_name if self.device_conf_filename: buf_params["DeviceConfigPath"] = self.device_conf_filename # Get phone properties for attrs in node.xpath(".//Parameter"): name, value, description = attrs.get("name"), attrs.get( "value"), attrs.get("description") if name in self.PROHIBITIVE_KEYS: # Do not allow to override internal keys # as it would lead to nasty & unexpected behavior !! continue # Not supported anymore, raise AcsConfigException.INVALID_BENCH_CONFIG !! if name and value: buf_params[name] = value self._bench_conf_deprecated[name] = (value, description) else: buf_params.update(attrs.attrib) # Report Errors if so if self.bench_contains_errors: LOGGER_FWK.error(self._report_errors()) _error( 'Invalid Bench Parameters format found! {0}'.format(', '.join( self._bench_conf_deprecated.keys())), AcsConfigException.INVALID_BENCH_CONFIG) # Extracting device modules if so buf_params.device_modules = self.extract_device_modules(node) return buf_params
def setup(self): """ Setup files which will be push to TCR server :rtype: tuple :return: Status and output log """ # Check original acs report path status = Global.FAILURE msg = "" if not os.path.exists(self.original_report_path): msg = "Cannot retrieve original ACS results: %s" % self.original_report_path LOGGER.error(msg) status = Global.FAILURE else: # compute the report folder name (used to generate html file) if not self.report_name: # If folder ending character is present, should remove it before treatment if self.original_report_path[-1:] in ('\\\\', '\\', '/'): self.report_name = str( os.path.basename(self.original_report_path[:-1])) else: self.report_name = str( os.path.basename(self.original_report_path)) if not self.new_report_path: (status, output) = self.build_file_name(self.original_report_path) if status == Global.SUCCESS: LOGGER.info("Building file name OK") # each push must have its own dedicated directory # it will be easier to manage reports cache like this : each folder # contains zip + md5 file + json info sub_folder_push = os.path.join( self.cache_reports, "%s_%s" % (CACHE_PUSH_BASE_FOLDER, time.strftime("%Y-%m-%d_%Hh%M.%S"))) self._lock_file = os.path.join(sub_folder_push, LOCK) if not os.path.isdir(sub_folder_push): os.makedirs(sub_folder_push) with open(self._lock_file, 'w') as lock: lock.write("locked") # output contains computed report dirname self.new_report_path = os.path.join( sub_folder_push, output) # create report zip file & md5 file associated status, msg = self.prepare_files() else: msg = "Building file name FAIL: %s" % output status = Global.FAILURE if status == Global.SUCCESS: msg = "TCR push SETUP : OK" return status, msg
def prepare_files(self): """ Create report zip file and md5 files :rtype: tuple :return: Status and output log """ # Zip folder and retrieve zip file path LOGGER.info("Create TCR archive") status, self.zip_acs_report_tcr = ZipFolderUtilities.zip_tcr_campaign_data( self.original_report_path, self.new_report_path, self._log_folders_and_files) return self.__zip_analysis(status, self.zip_acs_report_tcr)
def start(self): """ Upload md5 & report zip files to specified url Generate in report dir a html file which redirects to the TCR campaign result :rtype: tuple :return: Status and output log """ msg = "" (status, _) = self.upload_all_files() # Create the shortcut if status != Global.SUCCESS: status = Global.FAILURE msg = "Could not upload files to TCR website" LOGGER.info(msg) return status, msg
def safe_remove_file(file_path, max_retry=5): """ Removes the file. .. note:: Due to windows limitation, sometimes, the file cannot be removed immediately so implements retry loop. :type file_path: str :param file_path: The path to the folder/file to remove. :type max_retry: int :param max_retry: Max remove retry. :rtype: tuple :return: Status and output log """ remove_ok = False retry = 1 status = Global.FAILURE output = '' file_name = os.path.basename(file_path) while not remove_ok and retry <= max_retry: try: if os.path.isfile(file_path): os.remove(file_path) elif os.path.isdir(file_path): shutil.rmtree(file_path) remove_ok = True status = Global.SUCCESS output = 'Remove {0} OK after {1}/{2} tries'.format( file_name, str(retry), str(max_retry)) LOGGER.info(output) except Exception as ex: # pylint: disable=W0703 time.sleep(1) retry += 1 output = 'Fail to remove {0} after {1}/{2} tries ({ex})'.format( file_name, str(retry), str(max_retry), ex=ex) LOGGER.error(output) return status, output
def __zip_analysis(self, zip_status, zip_output): if zip_status != Global.SUCCESS: LOGGER.error( "Archiving file FAIL, please check log file for more details") status = Global.FAILURE else: LOGGER.info("Archiving file OK, zip file size is {}".format( self._get_file_size(zip_output))) # Create md5sum file and retrieve md5sum file path LOGGER.info( "Create md5sum file from previously created archive ...") (status, output) = self.create_md5sum_file(zip_output) if status == Global.SUCCESS: LOGGER.info("Md5sum file creation OK") self.md5_acs_report_tcr = output else: LOGGER.error( "Md5sum file creation FAIL, please check log file for more details" ) status = Global.FAILURE msg = "File are not ready to upload!" if status == Global.SUCCESS: msg = "File are ready to upload!" return status, msg
def Zip(folder, filename): status = Global.FAILURE if ON_POSIX: from distutils import archive_util try: out_file = archive_util.make_archive(filename, format="gztar", root_dir=folder) LOGGER.info( "folder {0} has been properly zipped as tarball {1}". format(folder, out_file)) status = Global.SUCCESS except Exception as ex: # pylint: disable=W0703 LOGGER.error( "ZIP_ERROR: An error occured during file zipping (%s)" % str(ex)) out_file = "" else: import zipfile try: filename = filename + '.zip' zip_file = zipfile.ZipFile(filename, 'w', allowZip64=True) LOGGER.info('Create zip file: {0}'.format(filename)) ZipFolderUtilities.addFolderToZip(zip_file, folder) LOGGER.info( "folder {0} has been properly zipped as {1}".format( folder, filename)) zip_file.close() status = Global.SUCCESS out_file = os.path.abspath(filename) except IOError as error: LOGGER.error('Cannot create zip file: {0} - {1}'.format( filename, error)) status = Global.FAILURE out_file = "" if status == Global.SUCCESS: LOGGER.info("ZIP FILE: {0}".format(out_file)) return status, out_file
def build_file_name(self, src_folder_path): """ Creates a file name as: <Hostname>_<user>_[file_name] :type src_folder_path: str :param src_folder_path: The original folder path :rtype: tuple :return: status and output log (final file name) """ # Compute the file name LOGGER.info("Build standardized file name ...") # If folder ending character is present, should remove it before treatment if src_folder_path[-1:] in ('\\\\', '\\', '/'): LOGGER.info( "Removing tailing character from folder name parameter") file_name = str(os.path.basename(src_folder_path[:-1])) else: file_name = str(os.path.basename(src_folder_path)) if file_name in (None, ''): return Global.FAILURE, "Unable to build file name from \"" + str( src_folder_path) + "\"" expr = "(?P<user>([\.-_0-9a-zA-Z]).*)@.*" # pylint: disable=W1401 # Test matching correct email matches_str = re.compile(expr).search(str(self.user_mail)) # Return True or False according matching result if matches_str is not None: user = matches_str.group("user") else: return Global.FAILURE, "Unable to compute user from email " + str( self.user_mail) output_file_name = '{0}_{1}_{2}'.format(str(socket.gethostname()), user, file_name) LOGGER.info("FILE NAME: %s" % output_file_name) return Global.SUCCESS, output_file_name