示例#1
0
        def nested():
            j11, j12, j13, j14, j15 = [job(i) for i in range(11, 16)]
            s2 = Scheduler(Sequence(j11, j12, j13), label="nested internal")
            j12.requires(j14)
            j13.requires(j15)

            j1, j2, j3, j4, j5 = [job(i) for i in range(1, 6)]
            s1 = Scheduler(Sequence(j1, s2, j3), label="nested top")
            j1.requires(j4)
            j1.requires(j11)

            s2.requires(j13)

            # j2 not included in sched, untouched
            j2.requires(j1)

            self.assertEqual(len(j12.required), 2)
            self.assertEqual(len(j13.required), 2)
            self.assertEqual(len(j1.required), 2)
            self.assertEqual(len(s2.required), 2)
            self.assertEqual(len(j3.required), 1)
            s1.sanitize()
            self.assertEqual(len(j12.required), 1)
            self.assertEqual(len(j13.required), 1)
            self.assertEqual(len(j1.required), 0)
            self.assertEqual(len(s2.required), 1)
            self.assertEqual(len(j3.required), 1)
示例#2
0
    def test_nested_cycles(self):

        watch = Watch()

        def job(i):
            return Job(co_print_sleep(watch, .2, f"job {i}"),
                       label=f"job{i}")
        js1, js2, js3 = [job(i) for i in range(11, 14)]
        s2 = Scheduler(Sequence(js1, js2, js3))

        j1, j3 = job(1), job(3)
        s1 = Scheduler(Sequence(j1, s2, j3))
        self.assertTrue(s1.check_cycles())

        # create cycle in subgraph
        js1.requires(js3)
        self.assertFalse(s1.check_cycles())

        # restore in OK state
        js1.requires(js3, remove=True)
        self.assertTrue(s1.check_cycles())

        # add cycle in toplevel
        j1.requires(j3)
        self.assertFalse(s1.check_cycles())

        # restore in OK state
        j1.requires(j3, remove=True)
        self.assertTrue(s1.check_cycles())

        # add one level down
        s3 = Scheduler()
        jss1, jss2, jss3 = [job(i) for i in range(111, 114)]
        Sequence(jss1, jss2, jss3, scheduler=s3)

        # surgery in s2; no cycles
        s2.remove(js2)
        s2.sanitize()
        s2.add(s3)
        s3.requires(js1)
        js3.requires(s3)
        self.assertTrue(s1.check_cycles())

        # add cycle in s3
        js1.requires(js3)
        self.assertFalse(s1.check_cycles())
示例#3
0
def prepare_testbed_scheduler(  # pylint: disable=r0913, r0914
        gateway: SshNode,
        load_flag: bool,
        experiment_scheduler: Scheduler,
        images_mapping,
        nodes_left_alone=None,
        sdrs_left_alone=None,
        phones_left_alone=None,
        verbose_jobs=False):
    """

    This function is designed as a standard way for experiments to warm up.
    Experimenters only need to write a scheduler that defines the behaviour
    of their core experiment, this function will add additional steps that
    take care of a) checking for a valid lease, b) load images on nodes, and
    c) turn off unused devices.

    It is generally desirable to write an experiment script that has a
    `--load/-l` boolean flag; typically, one would use the ``--load`` flag the
    first time that an experiment is launched during a given timeslot, while
    subsequent calls won't. That is the purpose of the ``load_flag`` below;
    when set to False, only step a) is performed, otherwise the resulting
    scheduler will go for the full monty.

    Parameters:
      gateway_sshnode: the ssh handle to the gateway
      load_flag(bool): if not set, only the lease is checked
      experiment_scheduler: core scheduler for the experiment
      images_mapping: a dictionary that specifies images to be loaded on nodes;
        see examples below
      nodes_left_alone: a list of node numbers that should be left intact,
        neither loaded nor turned off;
      phones_left_alone: a list of node numbers that should be left intact,
        i.e. not switched to airplane mode.

    Return :
      The overall scheduler where the input ``experiment_scheduler`` is embedded.

    Examples:
      Specify a mapping like the following::

          images_mapping = { "ubuntu" : [1, 4, 5], "gnuradio": [16]}

      Note that the format for ``images_mapping``, is flexible;
      if only one node is to be loaded, the iterable level is optional;
      also each node can be specified as an ``int``, a ``bytes``, a ``str``,
      in which case non numeric characters are ignored. So this is a legitimate
      requirement as well::

        images_mapping = { 'openair-cn': 12 + 4,
                           'openair-enodeb': ('fit32',),
                           'ubuntu': {12, 'reboot1', '004',
                                      'you-get-the-picture-34'}
        }
    """

    # handle default mutable args
    nodes_left_alone = set(nodes_left_alone) if nodes_left_alone else set()
    sdrs_left_alone = set(sdrs_left_alone) if sdrs_left_alone else set()
    phones_left_alone = set(phones_left_alone) if phones_left_alone else set()

    scheduler = Scheduler(label="Preparation")

    check_lease = SshJob(
        scheduler=scheduler,
        node=gateway,
        verbose=verbose_jobs,
        label="Check lease {}".format(gateway.username),
        command=Run("rhubarbe leases --check", label="rlease"),
    )

    # if no image loading is requested, we're done here
    if not load_flag:
        scheduler.add(experiment_scheduler)
        experiment_scheduler.requires(check_lease)
        return scheduler

    # otherwise, we want to do in parallel
    # (*) as many image-loading jobs as we have entries in images_mapping
    # (*) one job to turn off phones, nodes and usrps
    #     as parallelizing brings no speed up at all

    # todo ideally we could also probe the testbed to figure out which nodes
    # are currently unavailable, and let them alone as well; but well.

    # the jobs that we need to wait for before going on with the real stuff
    octopus = []

    loaded_nodes = set()

    for image, nodes in images_mapping.items():
        # let's be as flexible as possible
        # (1) empty node list should be fine
        if not nodes:
            continue
        # (2) atomic types should be allowed
        if isinstance(nodes, (int, str, bytes)):
            nodes = [nodes]
        # (3) accept all forms of inputs
        nodes = {r2lab_id(node) for node in nodes}
        duplicates = loaded_nodes & nodes
        if duplicates:
            print("WARNING - nodes in {} have been assigned several images".
                  format(duplicates))
        loaded_nodes.update(nodes)
        # for there on we need strings
        node_args = " ".join(str(node) for node in nodes)
        octopus.append(
            SshJob(
                gateway,
                scheduler=scheduler,
                required=check_lease,
                label=("loading {} on {}".format(image, node_args)),
                commands=[
                    Run("rhubarbe load -i {} {}".format(image, node_args)),
                    Run("rhubarbe wait {}".format(node_args)),
                ],
                verbose=verbose_jobs,
            ))

    ### turn off stuff
    # nodes
    dont_off_nodes = nodes_left_alone | loaded_nodes
    # do turn off usrp device even on loaded nodes
    dont_off_sdrs = sdrs_left_alone
    # phones - there's no equivalent of --all ~ notation with phones
    off_phones = set(range(1, PHONES+1)) \
                 - {r2lab_id(ph) for ph in phones_left_alone}

    r2lab_includes = [
        find_local_embedded_script(x) for x in ("faraday.sh", "r2labutils.sh")
    ]

    if off_phones:
        octopus.append(
            SshJob(gateway,
                   scheduler=scheduler,
                   required=check_lease,
                   critical=False,
                   commands=[
                       RunScript(find_local_embedded_script("faraday.sh"),
                                 "macphone{}".format(phone),
                                 "r2lab-embedded/shell/macphone.sh",
                                 "phone-off",
                                 label="turn off phone {}".format(phone),
                                 includes=r2lab_includes)
                       for phone in off_phones
                   ],
                   verbose=verbose_jobs))

    octopus.append(
        SshJob(
            gateway,
            scheduler=scheduler,
            required=check_lease,
            label="Turn off unused devices",
            commands=[
                Run(_rhubarbe_command(verb="off", left_alone=dont_off_nodes)),
                Run(_rhubarbe_command(verb="usrpoff",
                                      left_alone=dont_off_sdrs)),
            ],
            verbose=verbose_jobs,
        ))

    # embed experiment scheduler
    experiment_scheduler.requires(octopus)
    scheduler.add(experiment_scheduler)

    return scheduler