Пример #1
0
def sync(verbose=False):
    ignore_devices_on_servers_re = None
    if config.has_option('inventory', 'ignore_devices_on_servers_re'):
        ignore_devices_on_servers_re = config.get('inventory', 'ignore_devices_on_servers_re')
    from_inv = get_devices(
            config.get('inventory', 'url'),
            config.get('inventory', 'filter'),
            config.get('inventory', 'username'),
            config.get('inventory', 'password'),
            ignore_devices_on_servers_re,
            verbose=verbose)
    # dump the db second, since otherwise the mysql server can go away while
    # get_devices is still running, which is no fun
    from_db = data.dump_devices()

    for task in merge_devices(from_db, from_inv):
        if task[0] == 'insert':
            if verbose: print "insert device", task[1]['fqdn']
            data.insert_device(task[1])
        elif task[0] == 'delete':
            if verbose: print "delete device", task[2]
            data.delete_device(task[1])
        elif task[0] == 'update':
            if verbose: print "update device", task[2]
            data.update_device(task[1], task[2])
        else:
            raise AssertionError('%s is not a task' % task[0])
Пример #2
0
def sync(db, verbose=False, ship_it=False):
    ignore_devices_on_servers_re = None
    if config.has_option('inventory', 'ignore_devices_on_servers_re'):
        ignore_devices_on_servers_re = config.get('inventory', 'ignore_devices_on_servers_re')
    from_inv = get_devices(
            config.get('inventory', 'url'),
            config.get('inventory', 'filter'),
            config.get('inventory', 'username'),
            config.get('inventory', 'password'),
            ignore_devices_on_servers_re,
            verbose=verbose)
    # dump the db second, since otherwise the mysql server can go away while
    # get_devices is still running, which is no fun
    from_db = db.inventorysync.dump_devices()

    ## get a list of relay_boards derived from the inventory dump
    relay_boards_from_inv = get_relay_boards(from_inv)
    ## get existing relay_board list from DB
    relay_boards_from_db = db.inventorysync.dump_relays()

    # get the list of changes that need to be made
    tasks = list(merge_devices(from_db, from_inv))

    # If there are too many changes, bail out and await human interaction.
    # "Too many" means more than 5 and more than a tenth of the larger of the
    # set of devices currently in inventory and the set in the DB.  This is a
    # failsafe to keep the inventory sync from unexpectedly erasing all
    # devices.
    if len(tasks) > max(5, len(from_db) / 10, len(from_inv) / 10) and not ship_it:
        raise RuntimeError("%d changes: pass --ship-it to make these changes" % len(tasks))

    # start merging devices
    for task in tasks:
        if task[0] == 'insert':
            if verbose: print "insert device", task[1]['fqdn']
            db.inventorysync.insert_device(task[1])
        elif task[0] == 'delete':
            if verbose: print "delete device", task[2]
            db.inventorysync.delete_device(task[1])
        elif task[0] == 'update':
            if verbose: print "update device", task[2]
            db.inventorysync.update_device(task[1], task[2])
        else:
            raise RuntimeError('%s is not a task' % task[0])

    # start merging relay_boards
    for task in merge_relay_boards(relay_boards_from_db, relay_boards_from_inv):
        if task[0] == 'insert':
            if verbose: print "insert relay_board", task[1]['fqdn']
            db.inventorysync.insert_relay_board(task[1])
        elif task[0] == 'delete':
            if verbose: print "delete relay_board", task[2]
            db.inventorysync.delete_relay_board(task[1])
        elif task[0] == 'update':
            if verbose: print "update relay_board", task[2]
            db.inventorysync.update_relay_board(task[1], task[2])
        else:
            raise RuntimeError('%s is not a task' % task[0])
Пример #3
0
    def POST(self, device_name):
        args, body = templeton.handlers.get_request_parms()
        try:
            assignee = body['assignee']
            duration = int(body['duration'])
            image_name = body['image']
            environment = body.get('environment', 'any')
        except (KeyError, ValueError):
            raise web.badrequest()

        try:
            image = self.db.images.get(image_name)
        except exceptions.NotFound:
            raise web.notfound()

        boot_config = {}
        for k in image['boot_config_keys']:
            try:
                boot_config[k] = body[k]
            except KeyError:
                raise web.badrequest()

        request_id = self.db.requests.add(device_name, environment, assignee,
                duration, image['id'], boot_config)
        mozpool.mozpool.driver.handle_event(request_id, 'find_device', None)
        info = self.db.requests.get_info(request_id)
        info['url'] = "http://%s/api/request/%d/" % ((config.get('server', 'fqdn'), request_id))
        response_data = {'request': info}
        if self.db.requests.get_machine_state(request_id) == 'closed':
            raise ConflictJSON(response_data)
        return response_data
Пример #4
0
def request_config(request_id):
    conn = sql.get_conn()
    res = conn.execute(
        select(
            [
                model.requests.c.requested_device,
                model.requests.c.assignee,
                model.requests.c.expires,
                model.requests.c.boot_config,
            ],
            model.requests.c.id == request_id,
        )
    )
    row = res.fetchone()
    if row is None:
        raise NotFound

    request = {
        "id": request_id,
        "requested_device": row[0].encode("utf-8"),
        "assignee": row[1].encode("utf-8"),
        "expires": row[2].isoformat(),
        "boot_config": row[3].encode("utf-8"),
        "assigned_device": "",
        "url": "http://%s/api/request/%d/" % (config.get("server", "fqdn"), request_id),
    }

    assigned_device = get_assigned_device(request_id)
    if assigned_device:
        request["assigned_device"] = assigned_device
    return request
Пример #5
0
def create_request(requested_device, assignee, duration, boot_config):
    conn = sql.get_conn()
    server_id = conn.execute(
        select([model.imaging_servers.c.id],
               model.imaging_servers.c.fqdn == config.get(
                   'server', 'fqdn'))).fetchall()[0][0]
    reservation = {
        'imaging_server_id':
        server_id,
        'requested_device':
        requested_device,
        'assignee':
        assignee,
        'expires':
        datetime.datetime.utcnow() + datetime.timedelta(seconds=duration),
        'boot_config':
        json.dumps(boot_config),
        'state':
        'new',
        'state_counters':
        '{}'
    }

    res = conn.execute(model.requests.insert(), reservation)
    return res.lastrowid
Пример #6
0
def main():
    # templeton uses $PWD/../html to serve /, so put PWD in a subdirectory of
    # the directory containing our html data.  Easiest is to just change the the
    # html directory itself
    os.chdir(os.path.join(os.path.dirname(mozpool.__file__), 'html'))

    # Set up logging
    logging.basicConfig(stream=sys.stderr, level=logging.DEBUG,
            format="%(name)s %(levelname)s - [%(asctime)s] %(message)s")

    # ignore urllib3 informational logging
    urllib3_logger = logging.getLogger('requests.packages.urllib3')
    urllib3_logger.setLevel(logging.CRITICAL)


    # if we're running fake boards, start those up
    if config.get('testing', 'run_fakes'):
        from mozpool.test import fakedevices
        rack = fakedevices.Rack()
        rack.start()

    # start up the lifeguard driver
    # TODO: make this configurable, as well as poll freq
    mozpool.lifeguard.driver = devicemachine.LifeguardDriver()
    mozpool.lifeguard.driver.start()

    # start up the mozpool driver
    mozpool.mozpool.driver = requestmachine.MozpoolDriver()
    mozpool.mozpool.driver.start()

    app = get_app()
    app.run()
Пример #7
0
def request_config(request_id):
    conn = sql.get_conn()
    res = conn.execute(
        select([
            model.requests.c.requested_device, model.requests.c.assignee,
            model.requests.c.expires, model.requests.c.boot_config
        ], model.requests.c.id == request_id))
    row = res.fetchone()
    if row is None:
        raise NotFound

    request = {
        'id':
        request_id,
        'requested_device':
        row[0].encode('utf-8'),
        'assignee':
        row[1].encode('utf-8'),
        'expires':
        row[2].isoformat(),
        'boot_config':
        row[3].encode('utf-8'),
        'assigned_device':
        '',
        'url':
        'http://%s/api/request/%d/' %
        (config.get('server', 'fqdn'), request_id)
    }

    assigned_device = get_assigned_device(request_id)
    if assigned_device:
        request['assigned_device'] = assigned_device
    return request
Пример #8
0
 def _send_event(self, event, set_state=True):
     # conveniently, most state and event names match
     if set_state:
         self._set_state(event)
     fqdn = config.get('server', 'fqdn')
     url = 'http://%s/api/device/%s/event/%s/' % (fqdn, self.name, event)
     requests.get(url)
Пример #9
0
def request_config(request_id):
    conn = sql.get_conn()
    res = conn.execute(select([model.requests.c.requested_device,
                               model.requests.c.assignee,
                               model.requests.c.expires,
                               model.requests.c.environment,
                               model.images.c.name.label('image'),
                               model.requests.c.boot_config],
                              model.requests.c.id==request_id,
                              from_obj=[model.requests.join(model.images)]))
    row = res.fetchone()
    if row is None:
        raise NotFound

    request = {'id': request_id,
               'requested_device': row[0].encode('utf-8'),
               'assignee': row[1].encode('utf-8'),
               'expires': row[2].isoformat(),
               'environment': row[3].encode('utf-8'),
               'image': row[4].encode('utf-8'),
               'boot_config': row[5].encode('utf-8'),
               'assigned_device': '',
               'url': 'http://%s/api/request/%d/' %
               (config.get('server', 'fqdn'), request_id)}

    assigned_device = get_assigned_device(request_id)
    if assigned_device:
        request['assigned_device'] = assigned_device
    return request
Пример #10
0
    def contact_lifeguard(self):
        device_request_data = {}
        request_config = data.request_config(self.machine.request_id)

        # Determine if we are imaging or just rebooting.
        # We need to pass boot_config as a JSON string, but verify that it's
        # a non-null object.
        if json.loads(request_config['boot_config']):
            event = 'please_pxe_boot'
            device_request_data['boot_config'] = request_config['boot_config']
            # FIXME: differentiate between b2g builds and other (future) image
            # types.
            device_request_data['pxe_config'] = config.get('mozpool',
                                                           'b2g_pxe_config')
        else:
            event = 'please_power_cycle'

        device_url = 'http://%s/api/device/%s/event/%s/' % (
            data.get_server_for_device(request_config['assigned_device']),
            request_config['assigned_device'], event)

        # FIXME: make this asynchronous so slow/missing servers don't halt
        # the state machine.
        try:
            urllib.urlopen(device_url, json.dumps(device_request_data))
        except IOError:
            logs.request_logs.add(self.machine.request_id,
                                  "could not contact lifeguard server at %s" %
                                  device_url)
            return False
        return True
Пример #11
0
    def contact_lifeguard(self):
        device_request_data = {}
        request_config = data.request_config(self.machine.request_id)

        # Determine if we are imaging or just rebooting.
        # We need to pass boot_config as a JSON string, but verify that it's
        # a non-null object.
        if json.loads(request_config['boot_config']):
            event = 'please_pxe_boot'
            device_request_data['boot_config'] = request_config['boot_config']
            # FIXME: differentiate between b2g builds and other (future) image
            # types.
            device_request_data['pxe_config'] = config.get(
                'mozpool', 'b2g_pxe_config')
        else:
            event = 'please_power_cycle'

        device_url = 'http://%s/api/device/%s/event/%s/' % (
            data.get_server_for_device(request_config['assigned_device']),
            request_config['assigned_device'], event)

        # FIXME: make this asynchronous so slow/missing servers don't halt
        # the state machine.
        try:
            urllib.urlopen(device_url, json.dumps(device_request_data))
        except IOError:
            logs.request_logs.add(
                self.machine.request_id,
                "could not contact lifeguard server at %s" % device_url)
            return False
        return True
Пример #12
0
    def add(self,
            requested_device,
            environment,
            assignee,
            duration,
            image_id,
            boot_config,
            _now=datetime.datetime.utcnow):
        """
        Add a new request with the given parameters.  The state is set to
        'new'.

        Returns the ID of the new request.
        """
        server_id = self.db.execute(
            select([model.imaging_servers.c.id],
                   model.imaging_servers.c.fqdn == config.get(
                       'server', 'fqdn'))).fetchall()[0][0]
        request = {
            'imaging_server_id': server_id,
            'requested_device': requested_device,
            'environment': environment,
            'assignee': assignee,
            'expires': _now() + datetime.timedelta(seconds=duration),
            'image_id': image_id,
            'boot_config': json.dumps(boot_config),
            'state': 'new',
            'state_counters': '{}'
        }

        res = self.db.execute(model.requests.insert(), request)
        return res.lastrowid
Пример #13
0
 def _send_event(self, event, set_state=True):
     # conveniently, most state and event names match
     if set_state:
         self._set_state(event)
     fqdn = config.get('server', 'fqdn')
     url = 'http://%s/api/device/%s/event/%s/' % (fqdn, self.name, event)
     requests.get(url)
    def POST(self, device_name):
        args, body = templeton.handlers.get_request_parms()
        try:
            assignee = body['assignee']
            duration = int(body['duration'])
            image_name = body['image']
            environment = body.get('environment', 'any')
        except (KeyError, ValueError):
            raise web.badrequest()

        try:
            image = self.db.images.get(image_name)
        except exceptions.NotFound:
            raise web.notfound()

        boot_config = {}
        for k in image['boot_config_keys']:
            try:
                boot_config[k] = body[k]
            except KeyError:
                raise web.badrequest()

        request_id = self.db.requests.add(device_name, environment, assignee,
                                          duration, image['id'], boot_config)
        mozpool.mozpool.driver.handle_event(request_id, 'find_device', None)
        info = self.db.requests.get_info(request_id)
        info['url'] = "http://%s/api/request/%d/" % (
            (config.get('server', 'fqdn'), request_id))
        response_data = {'request': info}
        if self.db.requests.get_machine_state(request_id) == 'closed':
            raise ConflictJSON(response_data)
        return response_data
Пример #15
0
def sync(db, verbose=False):
    ignore_devices_on_servers_re = None
    if config.has_option('inventory', 'ignore_devices_on_servers_re'):
        ignore_devices_on_servers_re = config.get('inventory', 'ignore_devices_on_servers_re')
    from_inv = get_devices(
            config.get('inventory', 'url'),
            config.get('inventory', 'filter'),
            config.get('inventory', 'username'),
            config.get('inventory', 'password'),
            ignore_devices_on_servers_re,
            verbose=verbose)
    # dump the db second, since otherwise the mysql server can go away while
    # get_devices is still running, which is no fun
    from_db = db.inventorysync.dump_devices()

    ## get a list of relay_boards derived from the inventory dump
    relay_boards_from_inv = get_relay_boards(from_inv)
    ## get existing relay_board list from DB
    relay_boards_from_db = db.inventorysync.dump_relays()

    # start merging devices
    for task in merge_devices(from_db, from_inv):
        if task[0] == 'insert':
            if verbose: print "insert device", task[1]['fqdn']
            db.inventorysync.insert_device(task[1])
        elif task[0] == 'delete':
            if verbose: print "delete device", task[2]
            db.inventorysync.delete_device(task[1])
        elif task[0] == 'update':
            if verbose: print "update device", task[2]
            db.inventorysync.update_device(task[1], task[2])
        else:
            raise RuntimeError('%s is not a task' % task[0])

    # start merging relay_boards
    for task in merge_relay_boards(relay_boards_from_db, relay_boards_from_inv):
        if task[0] == 'insert':
            if verbose: print "insert relay_board", task[1]['fqdn']
            db.inventorysync.insert_relay_board(task[1])
        elif task[0] == 'delete':
            if verbose: print "delete relay_board", task[2]
            db.inventorysync.delete_relay_board(task[1])
        elif task[0] == 'update':
            if verbose: print "update relay_board", task[2]
            db.inventorysync.update_relay_board(task[1], task[2])
        else:
            raise RuntimeError('%s is not a task' % task[0])
Пример #16
0
 def wrapped(self, id, *args):
     try:
         server = data.get_server_for_request(id)
     except data.NotFound:
         raise web.notfound()
     if server != config.get('server', 'fqdn'):
         raise web.found("http://%s%s" % (server, web.ctx.path))
     return function(self, id, *args)
Пример #17
0
def _get_device_config_path(device_name):
    """
    Get the path where the PXE boot symlink should be placed
    for a specific device.
    """
    mac_address = data.mac_with_dashes(data.device_mac_address(device_name))
    symlink_dir = os.path.join(config.get('paths', 'tftp_root'), "pxelinux.cfg")
    return os.path.join(symlink_dir, "01-" + mac_address)
Пример #18
0
 def wrapped(self, id, *args):
     try:
         server = self.db.relay_boards.get_imaging_server(id)
     except exceptions.NotFound:
         raise web.notfound()
     if server != config.get('server', 'fqdn'):
         raise web.found("http://%s%s" % (server, web.ctx.path))
     return function(self, id, *args)
Пример #19
0
 def wrapped(self, id, *args):
     try:
         server = data.get_server_for_request(id)
     except data.NotFound:
         raise web.notfound()
     if server != config.get('server', 'fqdn'):
         raise web.found("http://%s%s" % (server, web.ctx.path))
     return function(self, id, *args)
Пример #20
0
def _get_device_config_path(mac_address):
    """
    Get the path where the PXE boot symlink should be placed
    for a specific device.
    """
    mac_address = util.mac_with_dashes(mac_address)
    symlink_dir = os.path.join(config.get('paths', 'tftp_root'), "pxelinux.cfg")
    return os.path.join(symlink_dir, "01-" + mac_address)
Пример #21
0
 def wrapped(self, id, *args):
     try:
         server = self.db.relay_boards.get_imaging_server(id)
     except exceptions.NotFound:
         raise web.notfound()
     if server != config.get('server', 'fqdn'):
         raise web.found("http://%s%s" % (server, web.ctx.path))
     return function(self, id, *args)
Пример #22
0
    def test_load(self, gethostbyname, getfqdn):
        getfqdn.return_value = 'server.example.com'
        gethostbyname.return_value = '1.2.3.4'

        cfg_file = os.path.join(self.tempdir, "cfg")
        open(cfg_file, "w").write(textwrap.dedent("""\
            [test]
            val1 = 1
            """))
        os.environ['MOZPOOL_CONFIG'] = cfg_file

        config._config = None
        config._load()

        # check defaults as well as the value in the config above
        self.assertEqual(config.get('server', 'fqdn'), 'server.example.com')
        self.assertEqual(config.get('server', 'ipaddress'), '1.2.3.4')
        self.assertEqual(config.get('test', 'val1'), '1')
Пример #23
0
 def GET(self, request_id):
     try:
         request_id = int(request_id)
         info = self.db.requests.get_info(request_id)
         info['url'] = "http://%s/api/request/%d/" % ((config.get('server', 'fqdn'), request_id))
         return info
     except ValueError:
         raise web.badrequest()
     except exceptions.NotFound:
         raise web.notfound()
 def GET(self, request_id):
     try:
         request_id = int(request_id)
         info = self.db.requests.get_info(request_id)
         info['url'] = "http://%s/api/request/%d/" % (
             (config.get('server', 'fqdn'), request_id))
         return info
     except ValueError:
         raise web.badrequest()
     except exceptions.NotFound:
         raise web.notfound()
Пример #25
0
 def setUp(self):
     super(TestDevicePing, self).setUp()
     add_server("server1")
     self.device_mac = "001122334455"
     add_device("device1", server="server1", state="running",
               mac_address=self.device_mac,
               relayinfo="relay-1:bank1:relay1")
     self.pxefile = "image1"
     # create a file for the boot image.
     open(os.path.join(config.get('paths', 'image_store'), self.pxefile), "w").write("abc")
     add_pxe_config("image1")
Пример #26
0
 def wrapped(self, id, *args):
     try:
         server = data.get_server_for_device(id)
     except data.NotFound:
         raise web.notfound()
     if server != config.get('server', 'fqdn'):
         raise web.found("http://%s%s" % (server, web.ctx.path))
     # otherwise, send an access-control header, so that pages in other domains can
     # call this API endpoint without trouble
     fqdns = data.all_imaging_servers()
     origins = [ 'http://%s' % fqdn for fqdn in fqdns ]
     web.header('Access-Control-Allow-Origin', ' '.join(origins))
     return function(self, id, *args)
Пример #27
0
 def _get_second_stage(self):
     mac_address = data.mac_with_dashes(data.device_mac_address(self.name))
     dir = os.path.join(config.get('paths', 'tftp_root'), "pxelinux.cfg")
     filename = os.path.join(dir, "01-" + mac_address)
     if os.path.exists(filename):
         with open(filename) as f:
             cfg = f.read()
             mo = re.search('mobile-imaging-url=[^ ]*/([^ ]*).sh', cfg)
             if mo:
                 return mo.group(1)
             else:
                 self.logger.warn('PXE config does not contain a mobile-imaging-url; not PXE booting')
     # if nothing's found, return None
     return None
Пример #28
0
def set_pxe(mac_address, pxe_config):
    """
    Set up the PXE configuration for the device as directed, substituting the
    server's IP address.  Note that this does *not* reboot the device.
    """
    # Write out the config file
    device_config_path = _get_device_config_path(mac_address)
    device_config_dir = os.path.dirname(device_config_path)
    if not os.path.exists(device_config_dir):
        os.makedirs(device_config_dir)

    # apply ipaddress substitution to the config contents
    pxe_config_contents = pxe_config.replace('%IPADDRESS%', config.get('server', 'ipaddress'))

    open(device_config_path, "w").write(pxe_config_contents)
Пример #29
0
 def wrapped(self, id, *args):
     try:
         server = self.db.devices.get_imaging_server(id)
     except exceptions.NotFound:
         raise web.notfound()
     if server != config.get('server', 'fqdn'):
         raise web.found("http://%s%s" % (server, web.ctx.path))
     # send an appropriate access-control header, if necessary
     origin = web.ctx.environ.get('HTTP_ORIGIN')
     if origin and origin.startswith('http://'):
         origin_hostname = origin[7:]
         fqdns = self.db.imaging_servers.list()
         if origin_hostname not in fqdns:
             raise web.Forbidden
         web.header('Access-Control-Allow-Origin', origin)
     return function(self, id, *args)
def set_pxe(mac_address, pxe_config):
    """
    Set up the PXE configuration for the device as directed, substituting the
    server's IP address.  Note that this does *not* reboot the device.
    """
    # Write out the config file
    device_config_path = _get_device_config_path(mac_address)
    device_config_dir = os.path.dirname(device_config_path)
    if not os.path.exists(device_config_dir):
        os.makedirs(device_config_dir)

    # apply ipaddress substitution to the config contents
    pxe_config_contents = pxe_config.replace('%IPADDRESS%',
                                             config.get('server', 'ipaddress'))

    open(device_config_path, "w").write(pxe_config_contents)
Пример #31
0
 def wrapped(self, id, *args):
     try:
         server = self.db.devices.get_imaging_server(id)
     except exceptions.NotFound:
         raise web.notfound()
     if server != config.get('server', 'fqdn'):
         raise web.found("http://%s%s" % (server, web.ctx.path))
     # send an appropriate access-control header, if necessary
     origin = web.ctx.environ.get('HTTP_ORIGIN')
     if origin and origin.startswith('http://'):
         origin_hostname = origin[7:]
         fqdns = self.db.imaging_servers.list()
         if origin_hostname not in fqdns:
             raise web.Forbidden
         web.header('Access-Control-Allow-Origin', origin)
     return function(self, id, *args)
Пример #32
0
 def _get_second_stage(self):
     mac_address = util.mac_with_dashes(
         self.db.devices.get_mac_address(self.name))
     dir = os.path.join(config.get('paths', 'tftp_root'), "pxelinux.cfg")
     filename = os.path.join(dir, "01-" + mac_address)
     if os.path.exists(filename):
         with open(filename) as f:
             cfg = f.read()
             mo = re.search('mobile-imaging-url=[^ ]*/([^ ]*).sh', cfg)
             if mo:
                 return mo.group(1)
             else:
                 self.logger.warn(
                     'PXE config does not contain a mobile-imaging-url; not PXE booting'
                 )
     # if nothing's found, return None
     return None
Пример #33
0
def create_request(requested_device, assignee, duration, boot_config):
    conn = sql.get_conn()
    server_id = conn.execute(
        select([model.imaging_servers.c.id], model.imaging_servers.c.fqdn == config.get("server", "fqdn"))
    ).fetchall()[0][0]
    reservation = {
        "imaging_server_id": server_id,
        "requested_device": requested_device,
        "assignee": assignee,
        "expires": datetime.datetime.utcnow() + datetime.timedelta(seconds=duration),
        "boot_config": json.dumps(boot_config),
        "state": "new",
        "state_counters": "{}",
    }

    res = conn.execute(model.requests.insert(), reservation)
    return res.lastrowid
Пример #34
0
def set_pxe(device_name, pxe_config_name, boot_config):
    """
    Set up the PXE configuration for the device as directed.  Note that this does *not*
    reboot the device.
    """
    logger.info('setting pxe config for %s to %s%s' % (device_name, pxe_config_name,
        ' with boot config' if boot_config else ''))
    image_details = data.pxe_config_details(pxe_config_name)['details']
    pxe_config_contents = image_details['contents']

    # Write out the config file
    device_config_path = _get_device_config_path(device_name)
    device_config_dir = os.path.dirname(device_config_path)
    if not os.path.exists(device_config_dir):
        os.makedirs(device_config_dir)

    # apply ipaddress substitution to the config contents
    pxe_config_contents = pxe_config_contents.replace('%IPADDRESS%', config.get('server', 'ipaddress'))

    open(device_config_path, "w").write(pxe_config_contents)
Пример #35
0
    def _populate(self):
        fqdn = config.get('server', 'fqdn')
        for dev_dict in self.db.devices.list(detail=True):
            # only emulate devices managed by this imaging sever
            if dev_dict['imaging_server'] != fqdn:
                continue

            # only emulate devices with relay info starting with 'localhost'
            hostname, bank, relay = dev_dict['relay_info'].rsplit(":", 2)
            if not hostname.startswith('localhost:'):
                continue

            if hostname not in self.chassis:
                self.chassis[hostname] = Chassis(self, hostname)
            chassis = self.chassis[hostname]

            device = Device(self, dev_dict['name'], dev_dict)
            self.devices[dev_dict['name']] = device
            self.devices_by_fqdn[dev_dict['fqdn']] = device
            chassis.add_device(int(bank[4:]), int(relay[5:]), device)
Пример #36
0
    def _populate(self):
        fqdn = config.get('server', 'fqdn')
        for dev_dict in self.db.devices.list(detail=True):
            # only emulate devices managed by this imaging sever
            if dev_dict['imaging_server'] != fqdn:
                continue

            # only emulate devices with relay info starting with 'localhost'
            hostname, bank, relay = dev_dict['relay_info'].rsplit(":", 2)
            if not hostname.startswith('localhost:'):
                continue

            if hostname not in self.chassis:
                self.chassis[hostname] = Chassis(self, hostname)
            chassis = self.chassis[hostname]

            device = Device(self, dev_dict['name'], dev_dict)
            self.devices[dev_dict['name']] = device
            self.devices_by_fqdn[dev_dict['fqdn']] = device
            chassis.add_device(int(bank[4:]), int(relay[5:]), device)
Пример #37
0
def create_request(requested_device, environment, assignee, duration, image_id,
                   boot_config):
    conn = sql.get_conn()
    server_id = conn.execute(select(
            [model.imaging_servers.c.id],
            model.imaging_servers.c.fqdn==config.get('server', 'fqdn'))
                             ).fetchall()[0][0]
    reservation = {'imaging_server_id': server_id,
                   'requested_device': requested_device,
                   'environment': environment,
                   'assignee': assignee,
                   'expires': datetime.datetime.utcnow() +
                              datetime.timedelta(seconds=duration),
                   'image_id': image_id,
                   'boot_config': json.dumps(boot_config),
                   'state': 'new',
                   'state_counters': '{}'}

    res = conn.execute(model.requests.insert(), reservation)
    return res.lastrowid
Пример #38
0
    def boot_b2g_second_stage(self):
        # get the boot_config and verify it
        fqdn = config.get('server', 'fqdn')
        url = 'http://%s/api/device/%s/bootconfig/' % (fqdn, self.name)
        r = requests.get(url)
        bootconfig = r.json()
        if 'b2gbase' not in bootconfig:
            self.logger('got invalid bootconfig - no b2gbase')
            raise Failure
        self.logger.debug('got b2gbase %r' % bootconfig['b2gbase'])

        self._send_event('b2g_downloading')
        self._wait(60, splay=30)
        self._send_event('b2g_extracting')
        self.sdcard_image = 'corrupt'
        self._wait(90, splay=60)
        self._send_event('b2g_rebooting')
        self._wait(3, splay=2) # time for the reboot command to do its thing
        self.pingable = False
        self.sdcard_image = 'b2g'
        self.boot_sdcard()
Пример #39
0
    def boot_b2g_second_stage(self):
        # get the boot_config and verify it
        fqdn = config.get('server', 'fqdn')
        url = 'http://%s/api/device/%s/bootconfig/' % (fqdn, self.name)
        r = requests.get(url)
        bootconfig = r.json()
        if 'b2gbase' not in bootconfig:
            self.logger('got invalid bootconfig - no b2gbase')
            raise Failure
        self.logger.debug('got b2gbase %r' % bootconfig['b2gbase'])

        self._send_event('b2g_downloading')
        self._wait(60, splay=30)
        self._send_event('b2g_extracting')
        self.sdcard_image = 'corrupt'
        self._wait(90, splay=60)
        self._send_event('b2g_rebooting')
        self._wait(3, splay=2)  # time for the reboot command to do its thing
        self.pingable = False
        self.sdcard_image = 'b2g'
        self.boot_sdcard()
def main(run=True):
    # templeton uses $PWD/../html to serve /, so put PWD in a subdirectory of
    # the directory containing our html data.  Easiest is to just change the the
    # html directory itself
    os.chdir(os.path.join(os.path.dirname(mozpool.__file__), 'html'))

    # Set up logging
    logging.basicConfig(
        stream=sys.stderr,
        level=logging.DEBUG,
        format="%(name)s %(levelname)s - [%(asctime)s] %(message)s")

    # ignore urllib3 informational logging
    urllib3_logger = logging.getLogger('requests.packages.urllib3')
    urllib3_logger.setLevel(logging.CRITICAL)

    logger = logging.getLogger('')
    logger.info('Mozpool-%s server starting' % mozpool.version)

    # set up the DB layer's facade
    db = db_setup()

    # if we're running fake boards, start those up
    if config.get('testing', 'run_fakes'):
        from mozpool.test import fakedevices
        rack = fakedevices.Rack(db)
        rack.start()

    # start up the lifeguard driver
    # TODO: make this configurable, as well as poll freq
    mozpool.lifeguard.driver = devicemachine.LifeguardDriver(db)
    mozpool.lifeguard.driver.start()

    # start up the mozpool driver
    mozpool.mozpool.driver = requestmachine.MozpoolDriver(db)
    mozpool.mozpool.driver.start()

    app = get_app(db)
    if run:
        app.run()
Пример #41
0
def get_engine():
    """
    Get a database engine object.
    """
    with _get_engine_lock:
        global engine
        if engine is None:
            engine_url = config.get('database', 'engine')

            # optimistically recycle connections after 10m
            engine = sqlalchemy.create_engine(engine_url, pool_recycle=600)
            # and pessimistically check connections before using them
            sqlalchemy.event.listen(engine.pool, 'checkout', _checkout_listener)

            # set sqlite to WAL mode to avoid weird concurrency issues
            if engine.dialect.name == 'sqlite':
                try:
                    engine.execute("pragma journal_mode = wal")
                except:
                    pass # oh well..

        return engine
Пример #42
0
def set_pxe(device_name, pxe_config_name, boot_config):
    """
    Set up the PXE configuration for the device as directed.  Note that this does *not*
    reboot the device.
    """
    logger.info('setting pxe config for %s to %s%s' % (device_name, pxe_config_name,
        ' with boot config' if boot_config else ''))
    image_details = data.pxe_config_details(pxe_config_name)['details']
    pxe_config_contents = image_details['contents']

    # Set the config in the database before writing to disk.
    data.set_device_config(device_name, pxe_config_name, boot_config)

    # Write out the config file
    device_config_path = _get_device_config_path(device_name)
    device_config_dir = os.path.dirname(device_config_path)
    if not os.path.exists(device_config_dir):
        os.makedirs(device_config_dir)

    # apply ipaddress substitution to the config contents
    pxe_config_contents = pxe_config_contents.replace('%IPADDRESS%', config.get('server', 'ipaddress'))

    open(device_config_path, "w").write(pxe_config_contents)
Пример #43
0
def get_engine():
    """
    Get a database engine object.
    """
    with _get_engine_lock:
        global engine
        if engine is None:
            engine_url = config.get('database', 'engine')

            # optimistically recycle connections after 10m
            engine = sqlalchemy.create_engine(engine_url, pool_recycle=600)
            # and pessimistically check connections before using them
            sqlalchemy.event.listen(engine.pool, 'checkout',
                                    _checkout_listener)

            # set sqlite to WAL mode to avoid weird concurrency issues
            if engine.dialect.name == 'sqlite':
                try:
                    engine.execute("pragma journal_mode = wal")
                except:
                    pass  # oh well..

        return engine
Пример #44
0
 def __init__(self, db, poll_frequency=statedriver.POLL_FREQUENCY):
     statedriver.StateDriver.__init__(self, db, poll_frequency)
     self.imaging_server_id = self.db.imaging_servers.get_id(
         config.get('server', 'fqdn'))
Пример #45
0
 def imaging_server_id(self):
     if self._imaging_server_id is None:
         self._imaging_server_id = self.db.imaging_servers.get_id(config.get('server', 'fqdn'))
     return self._imaging_server_id
def sync(db, verbose=False, ship_it=False):
    ignore_devices_on_servers_re = None
    if config.has_option('inventory', 'ignore_devices_on_servers_re'):
        ignore_devices_on_servers_re = config.get(
            'inventory', 'ignore_devices_on_servers_re')
    from_inv = get_devices(config.get('inventory', 'url'),
                           config.get('inventory', 'filter'),
                           config.get('inventory', 'username'),
                           config.get('inventory', 'password'),
                           ignore_devices_on_servers_re,
                           verbose=verbose)
    # dump the db second, since otherwise the mysql server can go away while
    # get_devices is still running, which is no fun
    from_db = db.inventorysync.dump_devices()

    ## get a list of relay_boards derived from the inventory dump
    relay_boards_from_inv = get_relay_boards(from_inv)
    ## get existing relay_board list from DB
    relay_boards_from_db = db.inventorysync.dump_relays()

    # get the list of changes that need to be made
    tasks = list(merge_devices(from_db, from_inv))

    # If there are too many changes, bail out and await human interaction.
    # "Too many" means more than 5 and more than a tenth of the larger of the
    # set of devices currently in inventory and the set in the DB.  This is a
    # failsafe to keep the inventory sync from unexpectedly erasing all
    # devices.
    if len(tasks) > max(5,
                        len(from_db) / 10,
                        len(from_inv) / 10) and not ship_it:
        raise RuntimeError("%d changes: pass --ship-it to make these changes" %
                           len(tasks))

    # start merging devices
    for task in tasks:
        if task[0] == 'insert':
            if verbose: print "insert device", task[1]['fqdn']
            db.inventorysync.insert_device(task[1])
        elif task[0] == 'delete':
            if verbose: print "delete device", task[2]
            db.inventorysync.delete_device(task[1])
        elif task[0] == 'update':
            if verbose: print "update device", task[2]
            db.inventorysync.update_device(task[1], task[2])
        else:
            raise RuntimeError('%s is not a task' % task[0])

    # start merging relay_boards
    for task in merge_relay_boards(relay_boards_from_db,
                                   relay_boards_from_inv):
        if task[0] == 'insert':
            if verbose: print "insert relay_board", task[1]['fqdn']
            db.inventorysync.insert_relay_board(task[1])
        elif task[0] == 'delete':
            if verbose: print "delete relay_board", task[2]
            db.inventorysync.delete_relay_board(task[1])
        elif task[0] == 'update':
            if verbose: print "update relay_board", task[2]
            db.inventorysync.update_relay_board(task[1], task[2])
        else:
            raise RuntimeError('%s is not a task' % task[0])
Пример #47
0
 def imaging_server_id(self):
     if self._imaging_server_id is None:
         self._imaging_server_id = self.db.imaging_servers.get_id(config.get("server", "fqdn"))
     return self._imaging_server_id
Пример #48
0
def setup(db_url=None):
    if not db_url:
        db_url = config.get('database', 'engine')
    return DB(db_url)
Пример #49
0
 def __init__(self, poll_frequency=statedriver.POLL_FREQUENCY):
     statedriver.StateDriver.__init__(self, poll_frequency)
     self.imaging_server_id = data.find_imaging_server_id(
         config.get('server', 'fqdn'))
 def imaging_server_id(self):
     if self._imaging_server_id is None:
         self._imaging_server_id = self.db.imaging_servers.get_id(
             config.get('server', 'fqdn'))
     return self._imaging_server_id
Пример #51
0
 def test_get_set(self):
     config.reset()
     config.set('abc', 'def', 'ghi')
     self.assertEqual(config.get('abc', 'def'), 'ghi')
     self.assertEqual(config.get('abc', 'XXX'), None)
     self.assertEqual(config.get('XXX', 'XXX'), None)