示例#1
0
def augment_experiment(experiment, extension, prefix=None):
    # XXX: prefixing is not complete,
    # all references to tasklists / targets within the
    # extension experiment should also be prefixed.

    res = experiment.xpath('/experiment/targets')
    if not res:
        raise ExperimentSyntaxError("Element 'targets' missing in experiment")
    targets = res[0]
    res = extension.xpath('/experiment/targets')
    if not res:
        raise ExperimentSyntaxError("Element 'targets' missing in experiment")
    ext_targets = res[0]
    if prefix is not None:
        for t in ext_targets:
            t.set('name', "{0}.{1}".format(prefix, t.get('name', '_unknown')))
    targets.extend(list(ext_targets))

    del targets, ext_targets

    res = experiment.xpath('/experiment/tasklists')
    if not res:
        raise ExperimentSyntaxError("Element 'tasklists' missing in experiment")
    tasklists = res[0]
    res = extension.xpath('/experiment/tasklists')
    if not res:
        raise ExperimentSyntaxError("Element 'tasklists' missing in experiment")
    ext_tasklists = res[0]
    if prefix is not None:
        for t in ext_tasklists:
            t.set('name', "{0}.{1}".format(prefix, t.get('name', '_unknown')))
    tasklists.extend(list(ext_tasklists))

    if extension.find('/experiment/steps'):
        logging.warn("Extension tasklist has 'steps'.  These steps will not be executed")
示例#2
0
def process_includes(experiment_xml, parent_filename, env=None, memo=None):
    includes = experiment_xml.xpath('/experiment/include')
    for el in includes:
        filename = el.get('file')
        if filename is None:
            raise ExperimentSyntaxError("Attribute 'file' missing in include")
        # XXX: Implement defaulting, shell style?
        filename = os.path.expandvars(filename)
        # Search relative paths relative to parent document
        if not os.path.isabs(filename):
            parent_dir = os.path.dirname(os.path.realpath(parent_filename))
            filename = os.path.join(parent_dir, filename)
        filename = os.path.realpath(filename)
        if memo is not None and filename in memo:
            raise ExperimentSyntaxError("recursive include detected")
        xml_parser = lxml.etree.XMLParser(remove_blank_text=True)
        # XXX: we only try one filename, but we may want to specify include
        # locations, like compilers do.
        extension_xml = lxml.etree.parse(filename, parser=xml_parser)
        # XXX: Maybe we want to keep the comments?
        # XXX: If that is then case, our processing logic needs to be more careful.
        lxml.etree.strip_elements(extension_xml, [lxml.etree.Comment])
        # XXX: some validation wouldn't hurt here
        new_memo = {filename}
        if memo is not None:
            new_memo.update(memo)
        establish_names(extension_xml)
        process_includes(extension_xml, filename, memo=new_memo)
        prefix = el.get('prefix')
        augment_experiment(experiment_xml, extension_xml, prefix)
示例#3
0
 def run_cleanup(self, tasklist_xml, tasklists_env, var_env):
     cleanup_task = None
     cleanup_name = tasklist_xml.get('cleanup')
     if cleanup_name is not None:
         cleanup_task = tasklists_env.get(cleanup_name)
         if cleanup_task is None:
             raise ExperimentSyntaxError("cleanup task %s not found\n" % (cleanup_name,))
     if cleanup_task is None:
         return
     #coro = self._run_list(cleanup_task, self.testbed, tasklists_env, var_env)
     coro = self.run_tasklist(cleanup_task, tasklists_env, var_env, None)
     try:
         yield from asyncio.async(coro)
     except asyncio.TimeoutError:
         # XXX: be more verbose!
         logging.warning(
                 "Cleanup tasklist %s on node %s timed out",
                 cleanup_name,
                 self.name)
     except StopExperimentException as e:
         logging.warning(
                 "Cleanup tasklist %s on node %s stopped (%s)",
                 cleanup_name,
                 self.name,
                 e.scope)
     except ExperimentExecutionError as e:
         logging.warning(
                 "Cleanup tasklist %s on node %s failed (%s)",
                 cleanup_name,
                 self.name,
                 e.message)
示例#4
0
    def _step_tasklist(self, step_xml, tasklists_env, var_env={}):
        targets_def = step_xml.get("targets")
        if targets_def is None:
            logging.warn("step has no targets, skipping")
            return
        tasklist_name = step_xml.get("tasklist")
        if tasklist_name is None:
            logging.warn("step has no tasklist, skipping")
            return
        tasklist = tasklists_env.get(tasklist_name)
        if tasklist is None:
            raise ExperimentSyntaxError("Tasklist '%s' not found" % (tasklist_name,))
        background = False
        bg_str = step_xml.get('background')
        if bg_str is not None and bg_str.lower() == 'true':
            background = True
        delay = get_delay_attr(step_xml, 'start')
        stop = get_delay_attr(step_xml, 'stop')
        logging.info("delay for step with tl %s is %s", tasklist_name, delay)

        composedEnv = {}
        composedEnv.update(var_env)
        composedEnv.update(helper.exportEnv(step_xml))
        
        self.schedule_tasklist(targets_def, tasklist, tasklists_env, background, delay, composedEnv, stop)
示例#5
0
    def _process_pl_slice(self, el):
        api_url = find_text(el, 'apiurl')
        if not api_url:
            raise ExperimentSyntaxError("Planetlab slice requires 'apiurl'")
        slicename = find_text(el, "slicename")
        if not slicename:
            raise ExperimentSyntaxError("Planetlab slice requires 'slicename'")

        groupname = el.get("name")
        if groupname is None:
            groupname = slicename

        server = xmlrpc.client.ServerProxy(api_url)

        user = find_text(el, 'user')
        if user is None:
            raise ExperimentSyntaxError("Planetlab slice requires 'user'")
        pw = find_text(el, 'password')
        if pw is None:
            # XXX: check if interaction is allowed
            pw = getpass.getpass("Planetlab Password: "******"password"

        logging.info("Making RPC call to planetlab")

        try:
            node_ids = server.GetSlices(auth, [slicename], ['node_ids'])[0]['node_ids']

            node_hostnames = [node['hostname'] for node in server.GetNodes(auth, node_ids, ['hostname'])]
        except Exception as e:
            raise ExperimentSetupError("PlanetLab API call failed")

        logging.info("Got response from planetlab")

        members = []
        for num, hostname in enumerate(node_hostnames):
            name = "_pl_" + slicename + "." + str(num)
            cfg = E.target(
                    {"type": "ssh", "name": name},
                    E.host(hostname), E.user(slicename))
            self.nodes[name] = SSHNode(cfg, testbed=self)
            members.append(name)
        self.groups[groupname] = members
示例#6
0
    def __init__(self, node_xml, testbed):
        super().__init__(node_xml, testbed)
        if self.name is None:
            raise ExperimentSyntaxError("Node name must be given")
        self.host = find_text(node_xml, 'host')
        if self.host is None:
            raise ExperimentSyntaxError("SSH target requires host")
        self.user = find_text(node_xml, 'user')
        if self.user is None:
            raise ExperimentSyntaxError("SSH target requires user")
        self.port = find_text(node_xml, 'port')
        extra = find_text(node_xml, 'extra-args')
        if extra is None:
            self.extra = []
        else:
            self.extra = shlex.split(extra)

        if self.port is None:
            self.port = 22
        self.target = "%s@%s" % (self.user, self.host)
示例#7
0
    def __init__(self, experiment_xml, settings):
        self.experiment_xml = experiment_xml
        self.settings = settings
        self.targets = self.experiment_xml.findall('/targets/target')
        self.steps = self.experiment_xml.find("steps")
        if self.steps is None:
            raise ExperimentSyntaxError("Element 'steps' missing.  Did you try to execute an extension library?")
        self.tasklists_env = {}

        for x in experiment_xml.xpath("/experiment/tasklists/tasklist[@name]"):
            self.tasklists_env[x.get('name')] = x
示例#8
0
 def _process_group(self, els):
     members = []
     for el in els:
         refname = el.get('ref')
         if refname is not None:
             members.append(refname)
             continue
         name = el.get('name')
         if name is None:
             raise ExperimentSyntaxError("target must have ref or name")
         members.append(name)
         # XXX: pass environment of group down to the peers of the group
         self._process_declaration(el)
     self.groups[els.get('name')] = members
示例#9
0
    def _step_teardown(self, step_xml, tasklists_env, var_env):
        targets_def = step_xml.get("targets")
        if targets_def is None:
            logging.warn("register-teardown has no targets, skipping")
            return
        tasklist_name = step_xml.get("tasklist")
        if tasklist_name is None:
            logging.warn("register-teardown has no tasklist, skipping")
            return
        tasklist = tasklists_env.get(tasklist_name)
        if tasklist is None:
            raise ExperimentSyntaxError("Tasklist '%s' not found" % (tasklist_name,))
        logging.info("Registering teardown for '%s' on '%s'", tasklist_name, targets_def)

        composedEnv = {}
        composedEnv.update(var_env)
        composedEnv.update(helper.exportEnv(step_xml))

        self.testbed.teardowns.append((targets_def, tasklist, composedEnv))
示例#10
0
    def run_tasklist(self, tasklist_xml, tasklists_env, var_env, stop_time):
        list_name = tasklist_xml.get('name', '(unnamed)')
        logging.info("running tasklist '%s'", list_name)
        # actual tasks, with declarations stripped
        error_policy = tasklist_xml.get('on-error')
        if error_policy is None:
            error_policy = 'stop-tasklist'
        timeout_str = tasklist_xml.get('timeout')
        timeout = stop_time
        if timeout_str is not None:
            timeout_tl = isodate.parse_duration(timeout_str).total_seconds()
            if timeout is None:
                timeout = timeout_tl
            else:
                times = [timeout_tl, timeout]
                timeout = min(times)

        coro = self._run_list(tasklist_xml, self.testbed, tasklists_env, var_env)
        try:
            logging.info(
                    "Running tasklist %s with timeout of %s.",
                    list_name, timeout)
            yield from asyncio.wait_for(asyncio.async(coro), timeout)
        except asyncio.TimeoutError:
            # XXX: be more verbose!
            logging.warning(
                    "Tasklist %s on node %s timed out",
                    list_name,
                    self.name)
            # XXX: cleanup!
        except StopExperimentException as e:
            if e.scope == 'stop-experiment':
                raise
            # otherwise, we ignore the exception
        except ExperimentExecutionError as e:
            logging.error("Tasklist execution (%s on %s) raised exception (%s)", list_name, self.name, e.message)
            if error_policy in ('stop-experiment', 'stop-step', 'stop-tasklist'):
                yield from self.run_cleanup(tasklist_xml, tasklists_env, var_env)
                raise StopExperimentException(error_policy)
            else:
                raise ExperimentSyntaxError("Unexpected error policy '%s'" % (error_policy,))
        yield from self.run_cleanup(tasklist_xml, tasklists_env, var_env)
示例#11
0
 def run_loop_listing(self, loop_xml, tasklists_env, listing, listParam, var_env):
     nested_ec = ExecutionContext(self.testbed)
     
     if ":" in listing:
         rangeGiven = listing.split(":")
         if len(rangeGiven) == 2 and helper.isInt(rangeGiven[0]) and helper.isInt(rangeGiven[1]):
             loopList = range(int(rangeGiven[0]), int(rangeGiven[1])+1)
             loopList = map(str, loopList)                
         else:
             raise ExperimentSyntaxError("Invalid Range declaration '%s'" % (loop_xml.tag,))
     else:
         loopList = listing.split(" ")
     for x in loopList:
         loop_env = {}
         loop_env[listParam] = x
         composedEnv = {}
         composedEnv.update(var_env)
         composedEnv.update(loop_env)
         nested_ec.var = composedEnv
         for step in list(loop_xml):
             yield from nested_ec.run_step(step, tasklists_env, composedEnv)
         yield from nested_ec.join()
示例#12
0
    def _run_task(self, task_xml, testbed, tasklists_env, var_env):
        name = task_xml.get('name', '(unnamed-task)')
        if task_xml.get('enabled', 'true').lower() == 'false':
            logging.info("Task %s disabled", name)
            return
        if task_xml.tag == 'run':
            yield from self._run_task_run(task_xml, var_env)
            return
        if task_xml.tag == 'get':
            source = find_text(task_xml, 'source')
            destination = find_text(task_xml, 'destination')
            # XXX: Just replace all environment variables
            source = source.replace("$GPLMT_TARGET", self.name)
            destination = destination.replace("$GPLMT_TARGET", self.name)
            yield from self.get(source, destination)
            return
        if task_xml.tag == 'put':
            source = find_text(task_xml, 'source')
            destination = find_text(task_xml, 'destination')
            kp_str = task_xml.attrib.get("keep")
            # XXX: Just replace all environment variables
            source = source.replace("$GPLMT_TARGET", self.name)
            destination = destination.replace("$GPLMT_TARGET", self.name)

            if kp_str is None or kp_str.lower() == 'false':
                #Check for invalid characters, whitelisting
                valid = re.compile("^([\.a-zA-Z][\-\.a-zA-Z]+)$")
                if valid.match(destination):
                    composedEnv = []
                    tasklist = lxml.etree.Element("tasklist", name="cleanup")#on error?
                    child1 = lxml.etree.SubElement(tasklist, "seq")
                    child2 = lxml.etree.SubElement(child1, "run", name="_anon2")
                    child2.text = ("rm " + destination)
                    self.testbed.teardowns.append((self.name, tasklist, composedEnv))
                else:
                    logging.warning("no automated removal, invalid characters in destination: %s", destination)

            yield from self.put(source, destination)
            return
        if task_xml.tag in ('sequence', 'seq'):
            for child_task in task_xml:
                yield from self._run_task(child_task, testbed, tasklists_env, var_env)
            return
        if task_xml.tag == 'fail':
            raise ExperimentExecutionError("user-requested fail")
        if task_xml.tag == 'call':
            tl = task_xml.get('tasklist')
            if tl is None:
                raise ExperimentSyntaxError("no tasklist name in 'call'")
            tasklist_xml = tasklists_env.get(tl)
            if tasklist_xml is None:
                raise ExperimentSyntaxError("Tasklist '%s' not defined" % (tl,))
            yield from self.run_tasklist(tasklist_xml, tasklists_env, var_env)
        if task_xml.tag in ('par', 'parallel'):
            parallel_tasks = []
            for child_task in task_xml:
                coro = self._run_task(child_task, testbed, tasklists_env, var_env)
                task = asyncio.async(coro)
                parallel_tasks.append(task)
            done, pending = yield from asyncio.wait(parallel_tasks)
            for task in done:
                task.result()
            return
示例#13
0
 def run_step(self, step_xml, tasklists_env, var_env={}):
     if step_xml.tag not in self._step_table:
         raise ExperimentSyntaxError("Invalid step '%s'" % (step_xml.tag,))
     step_method = self._step_table[step_xml.tag]
     yield from step_method(self, step_xml, tasklists_env, var_env)