def del_binding(self, fpath): """ Delete a binding between a CFG and a SUT. Return True/ False. """ logFull('xmlparser:del_binding') cfg_file = '{}/twister/config/bindings.xml'.format(userHome(self.user)) if not os.path.isfile(cfg_file): err = '*ERROR* Bindings Config file `{}`, for user `{}` does not exist!'.format(cfg_file, self.user) logError(err) return err bind_xml = parseXML(self.user, cfg_file) if bind_xml is None: err = '*ERROR* Config file `{}`, for user `{}` cannot be parsed!'.format(cfg_file, self.user) return err # Find the binding found = bind_xml.xpath('/root/binding/name[text()="{}"]/..'.format(fpath)) # If found, delete it if found: bind_xml.remove(found[0]) logDebug('Removed binding `{}`, for user `{}`.'.format(fpath, self.user)) else: err = '*WARN* Invalid binding `{}`, user `{}`! Cannot unbind!'.format(fpath, self.user) # logDebug(err) return False return dumpXML(self.user, cfg_file, bind_xml)
def get_resource(self, query, resource=None): """ Get the resource from resource by path or id. We can search for query in self.resources or in any other resource given as parameter. """ logFull("CeCommonAllocator: Getting the resource {}".format(query)) if not resource: resource = self.resources if not resource or not query: msg = 'Cannot get a null resource {} or a new query {}!'.format( resource, query) logError(msg) return False if ':' in query: query = query.split(':')[0] # If the query is an ID if '/' not in query: result = self.get_id(query, resource) else: result = self.get_path(query, resource) if result: return dict(result) return result
def getBindingsConfig(self): """ Parse the bindings file that connects Roots from a config file, with SUTs. """ logFull('xmlparser:getBindingsConfig') cfg_file = '{}/twister/config/bindings.xml'.format(userHome(self.user)) bindings = {} if not os.path.isfile(cfg_file): err = '*ERROR* Bindings Config file `{}`, for user `{}` does not exist!'.format(cfg_file, self.user) logError(err) return err bind_xml = parseXML(self.user, cfg_file) if bind_xml is None: err = '*ERROR* Config file `{}`, for user `{}` cannot be parsed!'.format(cfg_file, self.user) return err for binding in bind_xml.xpath('/root/binding'): name = binding.find('name') # Valid names ? if name is None: continue name = name.text.strip() if not name: continue bindings[name] = {} # All binds cfg -> sut for bind in binding.findall('bind'): cfg = bind.get('config') sut = bind.get('sut') bindings[name][cfg] = sut logDebug('Found `{}` bindings for user `{}`.'.format(len(bindings), self.user)) return bindings
def get_binding(self, fpath): """ Read a binding between a CFG and a SUT. The result is XML. """ logFull('xmlparser:get_binding') cfg_file = '{}/twister/config/bindings.xml'.format(userHome(self.user)) if not os.path.isfile(cfg_file): err = '*ERROR* Bindings Config file `{}`, for user `{}` does not exist!'.format(cfg_file, self.user) logError(err) return err bind_xml = parseXML(self.user, cfg_file) if bind_xml is None: err = '*ERROR* Config file `{}`, for user `{}` cannot be parsed!'.format(cfg_file, self.user) return err # Find the old binding found = bind_xml.xpath('/root/binding/name[text()="{}"]/..'.format(fpath)) if found: xml_string = etree.tostring(found[0]) return xml_string.replace('binding>', 'root>') else: logWarning('*ERROR* Cannot find binding name `{}` for user {}!'.format(fpath, self.user)) return False
def service_stop(self, service): """ stop service """ logFull('CeServices:service_stop') rc = self.service_status(service) if not rc: logDebug('SM: Service name `{}` is not running.'.format(service['name'])) return False tprocess = service.get('pid', 0) if isinstance(tprocess, int): logError('SM: Cannot stop service `{}`!'.format(service['name'])) try: tprocess.terminate() except Exception as exp_err: logError('SM: Cannot stop service: `{}`, exception `{}`!'.format(service['name'], exp_err)) return False try: time.sleep(0.1) os.killpg(tprocess.pid, signal.SIGTERM) time.sleep(0.1) tprocess.kill() except: pass logWarning('SM: Stopped service: `{}`.'.format(service['name'])) return True
def __init__(self): FsBorg.__init__(self) self.name = 'Local' if os.getuid(): logError( '{} FS: Central Engine must run as ROOT in order to start the User Service!' .format(self.name)) logInfo('Created {} FS.'.format(self.name))
def generate_xml(self, user, filename): ''' Receives project file. Creates testsuites.xml file by multiplying tests depending on the suts number and eps. ''' logDebug("CeParser: preparing to convert project file: `{}`,\ user `{}`.".format(filename, user)) data = self.project.read_project_file(user, filename) if data.startswith('*ERROR*'): logWarning(data) return data # try to parse the project file try: xml = etree.fromstring(data) except Exception as e: msg = "The file: '{}' it's not an xml file. Try again!\n{}".\ format(filename, e) logDebug(msg) return '*ERROR* ' + msg self._expand_global_configs(user, xml) self._expand_repeat_tag(xml) self._expand_by_ep(user, xml) repeated_dict = {} self._change_ids(xml, repeated_dict) self._resolve_dependencies(xml, repeated_dict) self._inherit_suite_dependency(xml) self._remove_empty_suites_tc(xml) for suite in xml.findall('.//TestSuite'): prop = suite.find('Property') if prop: suite.remove(prop) # Final XML string xml_string = etree.tostring(xml, pretty_print=True) # write the xml file xml_file = userHome(user) + '/twister/config/testsuites.xml' resp = self.project.localFs.write_user_file(user, xml_file, xml_string, 'w') if resp != True: logError(resp) return '*ERROR* ' + resp logDebug('CeParser: Successfully generated: `{}`, user `{}`.'.\ format(xml_file, user)) return True
def _expand_by_ep(self, user, xml): """ @param: user: the twister authenticated user xml: the project/last_edited xml in etree format. When going into recursion, xml becomes a suite. @summary: The sut tag can contain multiple suts and a sut can contain multiple eps. Main suites must be duplicated to run on each ep from each sut. """ suites = xml.findall('TestSuite') for suite in suites: # get all suts chosen by user all_suts = suite.find('SutName').text suite_name = suite.find('tsName').text if not all_suts: err = 'User `{}`: Invalid SUT for suite `{}`! Cannot generate project!'.format(user, suite_name) logWarning(err) return '*ERROR* ' + err suts_list = [q.replace('(', '.').replace(')', '') for q in all_suts.split(';') if q] # added get_sut to load the suts in resources dict. get_sut_info\ # takes resources from the resources dict index = xml.index(suite) for sut in suts_list: self.project.sut.get_sut(sut, props={'__user': user}) sut = '/' + sut sut_eps = self.project.sut.get_info_sut(sut + ':_epnames_' + user, {'__user': user}) if sut_eps and '*ERROR*' in sut_eps: logError(sut_eps) return sut_eps if sut_eps and (isinstance(sut_eps, str) or isinstance(sut_eps, unicode)): sut_eps_list = [ep for ep in sut_eps.split(';') if ep] for ep_id in sut_eps_list: deep_copy = copy.deepcopy(suite) suite_st = etree.tostring(deep_copy) suite_copy = etree.fromstring(suite_st) if suite_copy.find('TestCase') is not None or \ suite_copy.find('TestSuite') is not None: index += 1 xml.insert(index, suite_copy) self._edit_suite(ep_id, sut, suite_copy) else: # Find Anonimous EP in the active EPs anonim_ep = self.project._find_anonim_ep(user) if isinstance(anonim_ep, bool): return anonim_ep deep_copy = copy.deepcopy(suite) suite_st = etree.tostring(deep_copy) suite_copy = etree.fromstring(suite_st) if suite_copy.find('TestCase') is not None or \ suite_copy.find('TestSuite') is not None: index += 1 xml.insert(index, suite_copy) self._edit_suite(anonim_ep, sut, suite_copy) xml.remove(suite)
def parseXML(user, fname): """ Read 1 XML file via remote client and parse the content. """ data = localFs.read_user_file(user, fname) try: return etree.fromstring(data) except Exception as e: logError('Error parsing file `{}`, for user `{}`: `{}`!'.format(fname, user, e)) return None
def dumpXML(user, fname, tree): """ Write 1 XML file via remote client. """ data = etree.tostring(tree, pretty_print=True) try: return localFs.write_user_file(user, fname, data) except Exception as e: logError('Error dumping XML into file `{}`, for user `{}`: `{}`!'.format(fname, user, e)) return None
def save_config(self, service, data): """ Save configuration """ logFull('CeServices:save_config') config_path = '{0}/services/{1}/{2}'.format(TWISTER_PATH, service['name'], service['config']) if not os.path.isfile(config_path): logError('SM: No such config file `{0}`!'.format(config_path)) return False with open(config_path, 'wb') as out: out.write(data) return True
def read_config(self, service): """ Read configuration """ logFull('CeServices:read_config') config_path = '{0}/services/{1}/{2}'.format(TWISTER_PATH, service['name'], service['config']) if not os.path.isfile(config_path): logError('SM: No such config file `{0}`!'.format(config_path)) return False with open(config_path, 'rb') as out: data = out.read() return data or ''
def list_settings(self, xmlFile, xFilter=''): """ High level function for listing all settings from a Twister XML config file. """ logFull('xmlparser:list_settings') if not os.path.isfile(xmlFile): logError('User {}: Parse settings error! File path `{}` does not exist!'.format(self.user, xmlFile)) return False xmlSoup = parseXML(self.user, xmlFile) if xmlSoup is None: return [] if xFilter: return [x.tag for x in xmlSoup.xpath('//*') if xFilter in x.tag] else: return [x.tag for x in xmlSoup.xpath('//*')]
def updateConfigTS(self, files_config=''): """ Updates Test Suite Cofig file hash and recreates internal XML structure, only if the XML file is changed. The file number and suite number have to be unique. """ if files_config and (type(files_config) == type('') or type(files_config) == type(u'')) \ and (files_config[0] == '<' and files_config[-1] == '>'): # This is pure XML data config_ts = files_config # Hash check the XML file, to see if is changed newConfigHash = hashlib.md5(files_config).hexdigest() else: if not files_config or not os.path.isfile(files_config): # Get path to Test-Suites XML from Master config files_config = self.files_config if files_config.startswith('~/'): files_config = userHome(self.user) + files_config[1:] if not os.path.isfile(files_config): logError('User {}: Parser: Test-Suites XML file `{}` does '\ 'not exist! Please check framework config XML file!'.format(self.user, files_config)) self.configTS = None return -1 else: config_ts = localFs.read_user_file(self.user, files_config) # Hash check the XML file, to see if is changed newConfigHash = hashlib.md5(config_ts).hexdigest() if self.configHash != newConfigHash: logDebug('User {}: Parser: Test-Suites XML file changed, '\ 'rebuilding internal structure...\n'.format(self.user)) # Use the new hash self.configHash = newConfigHash # Create XML Soup from the new XML file try: self.configTS = etree.fromstring(config_ts) except Exception: logError('User {}: Parser ERROR: Cannot access Test-Suites XML data!'.format(self.user)) self.configTS = None return -1 self.files_config = files_config
def discard_release_reserved_resource(self, res_query, props={}): """ Discard changes for both SUTs and TestBeds """ user_info = self.user_info(props) user = user_info[0] logDebug( 'CeCommonAllocator: User {}: discard_release_reserved_resource: {}' .format(user, res_query)) if ':' in res_query: res_query = res_query.split(':')[0] # Having the id, we can discard and release directly if user in self.reservedResources.keys(): if res_query in self.reservedResources[user]: node_id = res_query # Having the name of the resource we have to get the id else: node = self.get_resource(res_query) if not isinstance(node, dict): msg = "Could not found a result for {}".format(res_query) logDebug(msg) return False # get the root parent of this resource if len(node['path']) > 1: node = self.get_path(node['path'][0], self.resources) if not node: logError('User {}: Cannot find resource path or ID `{}` !'. format(user, res_query)) return False node_id = node['id'] # Delete the entry from reserved dict try: self.reservedResources[user].pop(node_id) if not self.reservedResources[user]: self.reservedResources.pop(user) except Exception as exp_err: logError( 'CeCommonAllocator:discard_release_reserved_resource: `{}` for user {}!' .format(exp_err, user)) return False return True #RESOURCE_FREE
def del_settings_key(self, xmlFile, key, index=0): """ High level function for deleting a value from a Twister XML config file. If the `index` is specified and the `key` returns more values, only the index-th value is deleted; unless the `index` is -1, in this case, all values are deleted. """ logFull('xmlparser:del_settings_key') if not os.path.isfile(xmlFile): logError('User {}: Parse settings error! File path `{}` does not exist!'.format(self.user, xmlFile)) return False # The key must be string if not (isinstance(key, str) or isinstance(key, unicode)): return False # The index must be integer if not isinstance(index, int): return False # The key must not be Null if not key: return False else: key = str(key) xmlSoup = parseXML(self.user, xmlFile) if xmlSoup is None: return False xml_key = xmlSoup.xpath(key) if xml_key is None: return False # For index -1, delete all matches if index == -1: for xml_v in xml_key: xml_parent = xml_v.getparent() xml_parent.remove(xml_v) else: # Use the index-th occurence, or, if the index is wrong, exit try: xml_key = xml_key[index] except Exception: return False xml_parent = xml_key.getparent() xml_parent.remove(xml_key) return dumpXML(self.user, xmlFile, xmlSoup)
def is_resource_reserved(self, res_query, props={}): """ Verify if a resource is already reserved. Returns the user or false. """ logFull( 'CeCommonAllocator:is_resource_reserved: res_query = {}'.format( res_query)) resources = self.resources if '/' not in res_query: res_for_user = [ u for u in self.reservedResources if res_query in self.reservedResources[u] ] if '/' in res_query or not res_for_user: #if res_query contains components unsaved yet, search only for the TB if '/' in res_query: parts = [q for q in res_query.split('/') if q] node_path = self.get_resource('/' + parts[0]) else: node_path = self.get_resource(res_query) if not node_path or isinstance(node_path, str): msg = "No such resource {}".format(res_query) logError(msg) return False if isinstance(node_path['path'], list) and len(node_path['path']) > 1: node_path = self.get_path(node_path['path'][0], resources) if not node_path and isinstance(node_path, str): msg = "No such resource {}".format(res_query) logError(msg) return False res_for_user = [ u for u in self.reservedResources if node_path['id'] in self.reservedResources[u] ] if not res_for_user: return False return res_for_user[0]
def set_settings_value(self, xmlFile, key, value): """ High level function for setting a value in a Twister XML config file. """ logFull('xmlparser:set_settings_value') if not os.path.isfile(xmlFile): logError('User {}: Parse settings error! File path `{}` does not exist!'.format(self.user, xmlFile)) return False if not key: return False else: key = str(key) if not value: value = '' else: value = str(value) xmlSoup = parseXML(self.user, xmlFile) if xmlSoup is None: return False xml_key = xmlSoup.xpath(key) # If the key is found, update it if xml_key: xml_key[0].text = value # Else, create it else: # Try and split the key into parent and node if '/' in key: parent_path, node_name = '/'.join(key.split('/')[:-1]), key.split('/')[-1] else: parent_path, node_name = '/', key parent = xmlSoup.xpath(parent_path) # Invalid parent path ? if not parent: return False # Create the new node node = etree.Element(node_name) node.text = value node.tail = '\n' parent[0].insert(-1, node) return dumpXML(self.user, xmlFile, xmlSoup)
def getEmailConfig(self, eml_file=''): """ Returns the e-mail configuration. After Central Engine stops, an e-mail must be sent to the people interested. """ logFull('xmlparser:getEmailConfig') if not eml_file: eml_file = self.project_globals['eml_config'] if not os.path.isfile(eml_file): logError('User {}: Parser: E-mail Config file `{}` does not exist!'.format(self.user, eml_file)) return {} econfig = parseXML(self.user, eml_file) if econfig is None: return {} res = {} res['Enabled'] = '' res['SMTPPath'] = '' res['SMTPUser'] = '' res['SMTPPwd'] = '' res['From'] = '' res['To'] = '' res['Subject'] = '' res['Message'] = '' if econfig.xpath('Enabled/text()'): res['Enabled'] = econfig.xpath('Enabled')[0].text if econfig.xpath('SMTPPath/text()'): res['SMTPPath'] = econfig.xpath('SMTPPath')[0].text if econfig.xpath('SMTPUser/text()'): res['SMTPUser'] = econfig.xpath('SMTPUser')[0].text if econfig.xpath('SMTPPwd/text()'): res['SMTPPwd'] = econfig.xpath('SMTPPwd')[0].text if econfig.xpath('From/text()'): res['From'] = econfig.xpath('From')[0].text if econfig.xpath('To/text()'): res['To'] = econfig.xpath('To')[0].text if econfig.xpath('Subject/text()'): res['Subject'] = econfig.xpath('Subject')[0].text if econfig.xpath('Message/text()'): res['Message'] = econfig.xpath('Message')[0].text return res
def create_new_tb(self, name, parent=None, props={}): """ Create new test bed. Return the id of the new created tb. """ user_info = self.user_info(props) resources = self.resources if parent != '/' and parent != '1': msg = "The parent value is not root. Maybe you want to add a component\ to an existing SUT. Parent: {}".format(parent) logError(msg) return "*ERROR* " + msg props = self.valid_props(props) with self.acc_lock: # root can not be reserved so we just take it parent_p = self.get_resource('/', resources) if not parent_p or isinstance(parent_p, str): logFull("User: {} no result for query `{}`" .format(user_info[0], parent)) return None if '/' in name: logDebug('Stripping slash characters from `{}`...'.format(name)) name = name.replace('/', '') if name in self.resources['children']: msg = "User {}: A TB with name `{}` already exists!".format(user_info[0], name) logDebug(msg) return "*ERROR* " + msg # the resource doesn't exist - create it res_id = self.generate_index() parent_p['children'][name] = {'id': res_id, 'meta': props, 'children': {}, 'path': [name]} issaved = self.save_tb(props) if not issaved: msg = "User {}: Could not save TB `{}`".format(user_info[0], name) logDebug(msg) return "*ERROR* " + msg return res_id
def updateProjectGlobals(self): """ Returns the values of many global tags, from FWM and Test-Suites XML. """ logFull('xmlparser:updateProjectGlobals') if self.configTS is None: logError('User {}: Parser: Cannot get project globals, because'\ ' Test-Suites XML is invalid!'.format(self.user)) return False # Reset globals self.project_globals = OrderedDict() # Parse all known FWMCONFIG tags for tag_dict in FWMCONFIG_TAGS: # Create default entry self.project_globals[tag_dict['name']] = tag_dict['default'] # Update value from XML if self.xmlDict.xpath(tag_dict['tag'] + '/text()'): path = self.xmlDict.xpath(tag_dict['tag'])[0].text if path[0] == '~': path = self.user_home + path[1:] self.project_globals[tag_dict['name']] = path # Parse all known PROJECT tags for tag_dict in PROJECTCONFIG_TAGS: # Create default entry self.project_globals[tag_dict['name']] = tag_dict['default'] # Update value from XML if self.configTS.xpath(tag_dict['tag'] + '/text()'): # If the variable should be a Boolean if tag_dict.get('type') == 'bool': if self.configTS.xpath(tag_dict['tag'] + '/text()')[0].lower() == 'true': value = True else: value = False # If the variable should be a Number elif tag_dict.get('type') == 'number': value = self.configTS.xpath('round({})'.format(tag_dict['tag'])) else: value = self.configTS.xpath(tag_dict['tag'])[0].text self.project_globals[tag_dict['name']] = value return True
def set_binding(self, fpath, content): """ Write a binding between a CFG and a SUT. Return True/ False. """ logFull('xmlparser:set_binding') cfg_file = '{}/twister/config/bindings.xml'.format(userHome(self.user)) if not os.path.isfile(cfg_file): err = '*ERROR* Bindings Config file `{}`, for user `{}` does not exist!'.format(cfg_file, self.user) logError(err) return err bind_xml = parseXML(self.user, cfg_file) if bind_xml is None: err = '*ERROR* Config file `{}`, for user `{}` cannot be parsed!'.format(cfg_file, self.user) return err # Find the old binding found = bind_xml.xpath('/root/binding/name[text()="{}"]/..'.format(fpath)) # If found, use it if found: found = found[0] found.clear() # Or create it else: found = etree.SubElement(bind_xml, 'binding') name = etree.SubElement(found, 'name') name.text = fpath try: replace_xml = etree.fromstring(content, parser) except Exception: err = '*ERROR* Invalid XML content, user {}! Cannot parse!'.format(self.user) logWarning(err) return err for elem in replace_xml: found.append(elem) # Beautify XML ? return etree.tostring(bind_xml, pretty_print=True)
def get_settings_value(self, xmlFile, key): """ High level function for getting a value from a Twister XML config file. """ logFull('xmlparser:get_settings_value') if not os.path.isfile(xmlFile): logError('User {}: Parse settings error! File path `{}` does not exist!'.format(self.user, xmlFile)) return False if not key: return False else: key = str(key) xmlSoup = parseXML(self.user, xmlFile) if xmlSoup is None: return False if xmlSoup.xpath(key): txt = xmlSoup.xpath(key)[0].text return txt or '' else: return False
def getLogFileForType(self, logType): """ Returns the path for one type of log. CE will use this path to write the log received from EP. """ logFull('xmlparser:getLogFileForType') logs_path = self.project_globals['logs_path'] if not logs_path: logError('User {}: Parser: Logs path is not defined! Please ' \ 'check framework config XML file!'.format(self.user)) return {} logType = self._fixLogType(logType) logFile = self.xmlDict.xpath('//{0}/text()'.format(logType)) if logFile: return logs_path + os.sep + logFile[0] else: return ''
def save_tb(self, props={}): """ Function used to write the changes on HDD. """ logFull('CeTestBeds:_save {}'.format(props)) log = [] with self.save_lock: ver = self.resources.get('version', 0) self.resources['version'] = ver + 1 try: logDebug('Saving test bed file.') with open(self.res_file, 'w') as f_p: json.dump(self.resources, f_p, indent=4) except Exception as exp_err: log.append(exp_err) if log: logError(log) return '*ERROR* ' + str(log) return True
def __del__(self): """ Kill all services for a user. """ logInfo('Killing all services for the current CE.') for user in self._services: proc = self._services[user]['proc'] read_conn = self._services[user]['conn_read'] write_conn = self._services[user]['conn_write'] try: read_conn.close() except Exception as err: logError( 'Cannot close connection: `{}`, exception `{}`!'.format( read_conn, err)) try: write_conn.close() except Exception as err: logError( 'Cannot close connection: `{}`, exception `{}`!'.format( write_conn, err)) try: proc.terminate() except Exception as err: logError('Cannot stop service: `{}`, exception `{}`!'.format( proc, err)) try: time.sleep(0.1) os.killpg(proc.pid, signal.SIGTERM) time.sleep(0.1) proc.kill() except: pass
def load_tb(self, verbose=False): """ Parse resources file. """ logDebug('CeTestBeds:load_tb {}'.format(verbose)) with self.load_lock: if not self.resources.get('children'): self.resources = CONSTANT_DICTIONARY # try to load test bed resources file try: f_p = open(self.res_file, 'r') self.resources = json.load(f_p) f_p.close() del f_p if verbose: logDebug('TBs loaded successfully.') except Exception as exp_err: logError('Error loading TBs! {}'.format(exp_err)) is_res_modified = False # make older resources files that don't have 'path' compatible for res in self.resources.get('children'): self.resources['children'][res]['path'] = [res] modified = self.fix_path(self.resources['children'][res], [res]) if modified: is_res_modified = True # save the resources updated (with path field) for later usage if is_res_modified: issaved = self.save_tb() if isinstance(issaved, str): if issaved.startswith('*ERROR* '): msg = "We could not save this TB for user = {}.".format(user_info[0]) logDebug(msg) return "*ERROR* " + msg return self.resources
def valid_props(self, props): """ Verify if we have recevied valid props. """ logDebug('CeCommonAllocator: valid_props: props = {} '.format(props)) if not props: return dict() if isinstance(props, dict): return props elif isinstance(props, str) or isinstance(props, unicode): props = props.strip() try: props = ast.literal_eval(props) except Exception as exp_err: logError('Cannot parse properties: `{}`, `{}` !'.format( props, exp_err)) return None else: logError('Invalid properties for set method`{}` !'.format(props)) return False return props
def load_tb(self, verbose=False): """ Parse resources file. """ logDebug('CeTestBeds:load_tb {}'.format(verbose)) with self.load_lock: if not self.resources.get('children'): self.resources = constant_dictionary # try to load test bed resources file try: f = open(self.res_file, 'r') self.resources = json.load(f) f.close() del f if verbose: logDebug('TBs loaded successfully.') except Exception as e: logError('Error loading TBs! {}'.format(e)) # make older resources files that don't have 'path' compatible valid_resources = True for res in self.resources.get('children'): if not self.resources['children'][res].get('path', ''): valid_resources = False self.resources['children'][res]['path'] = [res] fix_path(self.resources['children'][res], [res]) # save the resources updated (with path field) for later usage if not valid_resources: issaved = self.save_tb() if isinstance(issaved, str): if issaved.startswith('*ERROR* '): msg = "We could not save this TB for user = {}.".format(user_info[0]) logDebug(msg) return "*ERROR* " + msg return self.resources
def getActiveEps(self): """ Returns a list with all EPs that appear in Test-Suites XML. """ logFull('xmlparser:getActiveEps') if self.configTS is None: logError('User {}: Parser ERROR: Cannot get active EPs, because' \ ' Test-Suites XML is invalid!'.format(self.user)) return [] activeEPs = [] for epname in self.configTS.xpath('//EpId/text()'): ep = str(epname).strip() # Ignore the empty EP names if not ep: continue # Don't add EP twice if ep in activeEPs: continue activeEPs.append(ep) return activeEPs