예제 #1
0
    def delete(self, instance_uuid=None, instance_from_db_virt=None):
        db.add_event('instance', instance_uuid, 'API DELETE', None, None, None)

        instance_networks = []
        for iface in list(db.get_instance_interfaces(instance_uuid)):
            if not iface['network_uuid'] in instance_networks:
                instance_networks.append(iface['network_uuid'])

        host_networks = []
        for inst in list(db.get_instances(local_only=True)):
            if not inst['uuid'] == instance_uuid:
                for iface in db.get_instance_interfaces(inst['uuid']):
                    if not iface['network_uuid'] in host_networks:
                        host_networks.append(iface['network_uuid'])

        instance_from_db_virt.delete()

        for network in instance_networks:
            n = net.from_db(network)
            if n:
                if network in host_networks:
                    with util.RecordedOperation('deallocate ip address',
                                                instance_from_db_virt) as _:
                        n.update_dhcp()
                else:
                    with util.RecordedOperation('remove network', n) as _:
                        n.delete()
예제 #2
0
    def _find_most_matching_networks(self, requested_networks, candidates):
        candidates_network_matches = {}
        for node in candidates:
            candidates_network_matches[node] = 0

            present_networks = []
            for inst in list(db.get_instances(only_node=node)):
                for iface in db.get_instance_interfaces(inst['uuid']):
                    if not iface['network_uuid'] in present_networks:
                        present_networks.append(iface['network_uuid'])

            for network in present_networks:
                if network in requested_networks:
                    candidates_network_matches[node] += 1

        candidates_by_network_matches = {}
        for node in candidates:
            matches = candidates_network_matches[node]
            candidates_by_network_matches.setdefault(matches, [])
            candidates_by_network_matches[matches].append(node)

        if len(candidates_by_network_matches) == 0:
            return candidates

        max_matches = max(candidates_by_network_matches.keys())
        return candidates_by_network_matches[max_matches]
예제 #3
0
파일: virt.py 프로젝트: mcarden/shakenfist
    def delete(self):
        with util.RecordedOperation('delete domain', self) as _:
            try:
                self.power_off()

                instance = self._get_domain()
                instance.undefine()
            except Exception:
                pass

        with util.RecordedOperation('delete disks', self) as _:
            try:
                shutil.rmtree(self.instance_path)
            except Exception:
                pass

        with util.RecordedOperation('release network addreses', self) as _:
            for ni in db.get_instance_interfaces(self.db_entry['uuid']):
                with db.get_lock('sf/ipmanager/%s' % ni['network_uuid'],
                                 ttl=120) as _:
                    ipm = db.get_ipmanager(ni['network_uuid'])
                    ipm.release(ni['ipv4'])
                    db.persist_ipmanager(ni['network_uuid'], ipm.save())

        db.update_instance_state(self.db_entry['uuid'], 'deleted')
        db.free_console_port(self.db_entry['console_port'])
        db.free_console_port(self.db_entry['vdi_port'])
예제 #4
0
파일: virt.py 프로젝트: mcarden/shakenfist
    def _create_domain_xml(self):
        """Create the domain XML for the instance."""

        if os.path.exists(self.xml_file):
            return

        with open(
                os.path.join(config.parsed.get('STORAGE_PATH'),
                             'libvirt.tmpl')) as f:
            t = jinja2.Template(f.read())

        networks = []
        for iface in list(db.get_instance_interfaces(self.db_entry['uuid'])):
            n = net.from_db(iface['network_uuid'])
            networks.append({
                'macaddr': iface['macaddr'],
                'bridge': n.subst_dict()['vx_bridge'],
                'model': iface['model']
            })

        # NOTE(mikal): the database stores memory allocations in MB, but the domain
        # XML takes them in KB. That wouldn't be worth a comment here if I hadn't
        # spent _ages_ finding a bug related to it.
        xml = t.render(uuid=self.db_entry['uuid'],
                       memory=self.db_entry['memory'] * 1024,
                       vcpus=self.db_entry['cpus'],
                       disks=self.db_entry['block_devices']['devices'],
                       networks=networks,
                       instance_path=self.instance_path,
                       console_port=self.db_entry['console_port'],
                       vdi_port=self.db_entry['vdi_port'])

        with open(self.xml_file, 'w') as f:
            f.write(xml)
예제 #5
0
    def delete(self):
        with util.RecordedOperation('delete domain', self):
            try:
                self.power_off()

                instance = self._get_domain()
                if instance:
                    instance.undefine()
            except Exception as e:
                util.ignore_exception('instance delete', e)

        with util.RecordedOperation('delete disks', self):
            try:
                if os.path.exists(self.instance_path):
                    shutil.rmtree(self.instance_path)
            except Exception as e:
                util.ignore_exception('instance delete', e)

        with util.RecordedOperation('release network addresses', self):
            for ni in db.get_instance_interfaces(self.db_entry['uuid']):
                db.update_network_interface_state(ni['uuid'], 'deleted')
                with db.get_lock('ipmanager',
                                 None,
                                 ni['network_uuid'],
                                 ttl=120,
                                 op='Instance delete'):
                    ipm = db.get_ipmanager(ni['network_uuid'])
                    ipm.release(ni['ipv4'])
                    db.persist_ipmanager(ni['network_uuid'], ipm.save())

        db.free_console_port(self.db_entry['console_port'])
        db.free_console_port(self.db_entry['vdi_port'])
예제 #6
0
    def _create_domain_xml(self):
        """Create the domain XML for the instance."""

        if os.path.exists(self.xml_file):
            return

        with open(
                os.path.join(config.parsed.get('STORAGE_PATH'),
                             'libvirt.tmpl')) as f:
            t = jinja2.Template(f.read())

        networks = []
        for iface in list(db.get_instance_interfaces(self.db_entry['uuid'])):
            n = net.from_db(iface['network_uuid'])
            networks.append({
                'macaddr': iface['macaddr'],
                'bridge': n.subst_dict()['vx_bridge']
            })

        xml = t.render(uuid=self.db_entry['uuid'],
                       memory=self.db_entry['memory'] * 1024,
                       vcpus=self.db_entry['cpus'],
                       disks=self.db_entry['block_devices']['devices'],
                       networks=networks,
                       network_model=config.parsed.get('NETWORK_MODEL'),
                       instance_path=self.instance_path,
                       console_port=self.db_entry['console_port'],
                       vdi_port=self.db_entry['vdi_port'])

        with open(self.xml_file, 'w') as f:
            f.write(xml)
예제 #7
0
파일: main.py 프로젝트: mcarden/shakenfist
def restore_instances():
    # Ensure all instances for this node are defined
    networks = []
    instances = []
    for inst in list(
            db.get_instances(only_node=config.parsed.get('NODE_NAME'))):
        for iface in db.get_instance_interfaces(inst['uuid']):
            if not iface['network_uuid'] in networks:
                networks.append(iface['network_uuid'])
        instances.append(inst['uuid'])

    with util.RecordedOperation('restore networks', None) as _:
        for network in networks:
            try:
                n = net.from_db(network)
                LOG.info('%s Restoring network' % n)
                n.create()
                n.ensure_mesh()
                n.update_dhcp()
            except Exception as e:
                LOG.error('%s Failed to restore network: %s' % (n, e))

    with util.RecordedOperation('restore instances', None) as _:
        for instance in instances:
            try:
                i = virt.from_db(instance)
                LOG.info('%s Restoring instance' % i)
                i.create()
            except Exception as e:
                LOG.error('%s Failed to restore instance: %s' % (i, e))
                db.update_instance_state(instance, 'error')
예제 #8
0
def instance_start(instance_uuid, network):
    log = LOG.withField('instance', instance_uuid)

    with db.get_lock('instance',
                     None,
                     instance_uuid,
                     ttl=900,
                     timeout=120,
                     op='Instance start') as lock:
        instance = virt.from_db(instance_uuid)

        # Collect the networks
        nets = {}
        for netdesc in network:
            if netdesc['network_uuid'] not in nets:
                n = net.from_db(netdesc['network_uuid'])
                if not n:
                    db.enqueue_instance_error(instance_uuid, 'missing network')
                    return

                nets[netdesc['network_uuid']] = n

        # Create the networks
        with util.RecordedOperation('ensure networks exist', instance):
            for network_uuid in nets:
                n = nets[network_uuid]
                try:
                    n.create()
                    n.ensure_mesh()
                    n.update_dhcp()
                except exceptions.DeadNetwork as e:
                    log.withField(
                        'network',
                        n).warning('Instance tried to use dead network')
                    db.enqueue_instance_error(
                        instance_uuid, 'tried to use dead network: %s' % e)
                    return

        # Allocate console and VDI ports
        instance.allocate_instance_ports()

        # Now we can start the instance
        libvirt = util.get_libvirt()
        try:
            with util.RecordedOperation('instance creation', instance):
                instance.create(lock=lock)

        except libvirt.libvirtError as e:
            code = e.get_error_code()
            if code in (libvirt.VIR_ERR_CONFIG_UNSUPPORTED,
                        libvirt.VIR_ERR_XML_ERROR):
                db.enqueue_instance_error(instance_uuid,
                                          'instance failed to start: %s' % e)
                return

        for iface in db.get_instance_interfaces(instance_uuid):
            db.update_network_interface_state(iface['uuid'], 'created')
예제 #9
0
    def delete(self,
               instance_uuid=None,
               instance_from_db=None,
               instance_from_db_virt=None):
        # Check if instance has already been deleted
        if instance_from_db['state'] == 'deleted':
            return error(404, 'instance not found')

        with db.get_lock('/sf/instance/%s' % instance_uuid) as _:
            db.add_event('instance', instance_uuid, 'api', 'delete', None,
                         None)

            instance_networks = []
            for iface in list(db.get_instance_interfaces(instance_uuid)):
                if not iface['network_uuid'] in instance_networks:
                    instance_networks.append(iface['network_uuid'])
                    db.update_network_interface_state(iface['uuid'], 'deleted')

            host_networks = []
            for inst in list(
                    db.get_instances(
                        only_node=config.parsed.get('NODE_NAME'))):
                if not inst['uuid'] == instance_uuid:
                    for iface in db.get_instance_interfaces(inst['uuid']):
                        if not iface['network_uuid'] in host_networks:
                            host_networks.append(iface['network_uuid'])

            instance_from_db_virt.delete()

            for network in instance_networks:
                n = net.from_db(network)
                if n:
                    if network in host_networks:
                        with util.RecordedOperation(
                                'deallocate ip address',
                                instance_from_db_virt) as _:
                            n.update_dhcp()
                    else:
                        with util.RecordedOperation('remove network', n) as _:
                            n.delete()
예제 #10
0
def instance_delete(instance_uuid):
    with db.get_lock('instance',
                     None,
                     instance_uuid,
                     timeout=120,
                     op='Instance delete'):
        db.add_event('instance', instance_uuid, 'queued', 'delete', None, None)

        # Create list of networks used by instance
        instance_networks = []
        for iface in list(db.get_instance_interfaces(instance_uuid)):
            if not iface['network_uuid'] in instance_networks:
                instance_networks.append(iface['network_uuid'])

        # Create list of networks used by all other instances
        host_networks = []
        for inst in list(
                db.get_instances(only_node=config.parsed.get('NODE_NAME'))):
            if not inst['uuid'] == instance_uuid:
                for iface in db.get_instance_interfaces(inst['uuid']):
                    if not iface['network_uuid'] in host_networks:
                        host_networks.append(iface['network_uuid'])

        instance_from_db_virt = virt.from_db(instance_uuid)
        if instance_from_db_virt:
            instance_from_db_virt.delete()

        # Check each network used by the deleted instance
        for network in instance_networks:
            n = net.from_db(network)
            if n:
                # If network used by another instance, only update
                if network in host_networks:
                    with util.RecordedOperation('deallocate ip address',
                                                instance_from_db_virt):
                        n.update_dhcp()
                else:
                    # Network not used by any other instance therefore delete
                    with util.RecordedOperation('remove network', n):
                        n.delete()
예제 #11
0
파일: net.py 프로젝트: mcarden/shakenfist
    def run(self):
        while True:
            time.sleep(30)

            # We do not reap unused networks from the network node, as they might be
            # in use for instances on other hypervisor nodes.
            if config.parsed.get('NODE_IP') != config.parsed.get('NETWORK_NODE_IP'):
                host_networks = []
                for inst in list(db.get_instances(only_node=config.parsed.get('NODE_NAME'))):
                    for iface in db.get_instance_interfaces(inst['uuid']):
                        if not iface['network_uuid'] in host_networks:
                            host_networks.append(iface['network_uuid'])

                for network in host_networks:
                    n = net.from_db(network)
                    n.ensure_mesh()
예제 #12
0
def restore_instances():
    # Ensure all instances for this node are defined
    networks = []
    instances = []
    for inst in list(
            db.get_instances(only_node=config.parsed.get('NODE_NAME'))):
        for iface in db.get_instance_interfaces(inst['uuid']):
            if not iface['network_uuid'] in networks:
                networks.append(iface['network_uuid'])
        instances.append(inst['uuid'])

    with util.RecordedOperation('restore networks', None):
        for network in networks:
            try:
                n = net.from_db(network)
                LOG.withObj(n).info('Restoring network')
                n.create()
                n.ensure_mesh()
                n.update_dhcp()
            except Exception as e:
                util.ignore_exception('restore network %s' % network, e)

    with util.RecordedOperation('restore instances', None):
        for instance in instances:
            try:
                with db.get_lock('instance',
                                 None,
                                 instance,
                                 ttl=120,
                                 timeout=120,
                                 op='Instance restore'):
                    i = virt.from_db(instance)
                    if not i:
                        continue
                    started = ['on', 'transition-to-on', 'initial', 'unknown']
                    if i.db_entry.get('power_state', 'unknown') not in started:
                        continue

                    LOG.withObj(i).info('Restoring instance')
                    i.create()
            except Exception as e:
                util.ignore_exception('restore instance %s' % instance, e)
                db.enqueue_instance_error(
                    instance,
                    'exception while restoring instance on daemon restart')
예제 #13
0
def instance_start(instance_uuid, network):
    with db.get_lock('instance', None, instance_uuid, ttl=900) as lock:
        instance = virt.from_db(instance_uuid)

        # Collect the networks
        nets = {}
        for netdesc in network:
            if netdesc['network_uuid'] not in nets:
                n = net.from_db(netdesc['network_uuid'])
                if not n:
                    db.enqueue_instance_delete(config.parsed.get('NODE_NAME'),
                                               instance_uuid, 'error',
                                               'missing network')
                    return

                nets[netdesc['network_uuid']] = n

        # Create the networks
        with util.RecordedOperation('ensure networks exist', instance):
            for network_uuid in nets:
                n = nets[network_uuid]
                n.create()
                n.ensure_mesh()
                n.update_dhcp()

        # Now we can start the isntance
        libvirt = util.get_libvirt()
        try:
            with util.RecordedOperation('instance creation', instance):
                instance.create(lock=lock)

        except libvirt.libvirtError as e:
            code = e.get_error_code()
            if code in (libvirt.VIR_ERR_CONFIG_UNSUPPORTED,
                        libvirt.VIR_ERR_XML_ERROR):
                db.enqueue_instance_delete(config.parsed.get('NODE_NAME'),
                                           instance_uuid, 'error',
                                           'instance failed to start')
                return

        for iface in db.get_instance_interfaces(instance_uuid):
            db.update_network_interface_state(iface['uuid'], 'created')
예제 #14
0
    def delete(self):
        with util.RecordedOperation('delete domain', self) as _:
            try:
                self.power_off()
            except:
                pass

        with util.RecordedOperation('delete disks', self) as _:
            try:
                shutil.rmtree(self.instance_path)
            except:
                pass

        with util.RecordedOperation('release network addreses', self) as _:
            for ni in db.get_instance_interfaces(self.db_entry['uuid']):
                n = net.from_db(ni['network_uuid'])
                n.ipmanager.release(ni['ipv4'])
                n.persist_ipmanager()

        db.delete_instance(self.db_entry['uuid'])
예제 #15
0
    def _find_most_matching_networks(self, requested_networks, candidates):
        if not candidates:
            return []

        # Find number of matching networks on each node
        candidates_network_matches = {}
        for node in candidates:
            candidates_network_matches[node] = 0

            # Make a list of networks for the node
            present_networks = []
            for inst in list(db.get_instances(only_node=node)):
                for iface in db.get_instance_interfaces(inst['uuid']):
                    if not iface['network_uuid'] in present_networks:
                        present_networks.append(iface['network_uuid'])

            # Count the requested networks present on this node
            for network in present_networks:
                if network in requested_networks:
                    candidates_network_matches[node] += 1

        # Store candidate nodes keyed by number of matches
        candidates_by_network_matches = {}
        for node in candidates:
            matches = candidates_network_matches[node]
            candidates_by_network_matches.setdefault(matches, []).append(node)

        # Find maximum matches of networks on a node
        max_matches = max(candidates_by_network_matches.keys())

        # Check that the maximum is not just the network node.
        # (Network node always has every network.)
        net_node = db.get_network_node()['fqdn']
        if (max_matches == 1
                and candidates_by_network_matches[max_matches][0] == net_node):
            # No preference, all candidates are a reasonable choice
            return candidates

        # Return list of candidates that has maximum networks
        return candidates_by_network_matches[max_matches]
예제 #16
0
def restore_instances():
    # Ensure all instances for this node are defined
    networks = []
    instances = []
    for inst in list(db.get_instances(local_only=True)):
        for iface in db.get_instance_interfaces(inst['uuid']):
            if not iface['network_uuid'] in networks:
                networks.append(iface['network_uuid'])
        instances.append(inst['uuid'])

    with util.RecordedOperation('restore networks', None) as _:
        for network in networks:
            LOG.info('Restoring network %s' % network)
            n = net.from_db(network)
            n.create()
            n.ensure_mesh()
            n.update_dhcp()

    with util.RecordedOperation('restore instances', None) as _:
        for instance in instances:
            LOG.info('Restoring instance %s' % instance)
            i = virt.from_db(instance)
            i.create()
예제 #17
0
    def _make_config_drive(self, disk_path):
        """Create a config drive"""

        # NOTE(mikal): with a big nod at https://gist.github.com/pshchelo/378f3c4e7d18441878b9652e9478233f
        iso = pycdlib.PyCdlib()
        iso.new(interchange_level=4,
                joliet=True,
                rock_ridge='1.09',
                vol_ident='config-2')

        # We're only going to pretend to do the most recent OpenStack version
        iso.add_directory('/openstack',
                          rr_name='openstack',
                          joliet_path='/openstack')
        iso.add_directory('/openstack/2017-02-22',
                          rr_name='2017-02-22',
                          joliet_path='/openstack/2017-02-22')
        iso.add_directory('/openstack/latest',
                          rr_name='latest',
                          joliet_path='/openstack/latest')

        # meta_data.json
        md = json.dumps({
            'random_seed':
            base64.b64encode(os.urandom(512)).decode('ascii'),
            'uuid':
            self.db_entry['uuid'],
            'availability_zone':
            config.parsed.get('ZONE'),
            'hostname':
            '%s.local' % self.db_entry['name'],
            'launch_index':
            0,
            'devices': [],
            'project_id':
            None,
            'name':
            self.db_entry['name'],
            'public_keys': {
                'mykey': self.db_entry['ssh_key']
            }
        }).encode('ascii')
        iso.add_fp(io.BytesIO(md),
                   len(md),
                   '/openstack/latest/meta_data.json;1',
                   rr_name='meta_data.json',
                   joliet_path='/openstack/latest/meta_data.json')
        iso.add_fp(io.BytesIO(md),
                   len(md),
                   '/openstack/2017-02-22/meta_data.json;2',
                   rr_name='meta_data.json',
                   joliet_path='/openstack/2017-02-22/meta_data.json')

        # user_data
        if self.db_entry['user_data']:
            user_data = base64.b64decode(self.db_entry['user_data'])
            iso.add_fp(io.BytesIO(user_data),
                       len(user_data),
                       '/openstack/latest/user_data',
                       rr_name='user_data',
                       joliet_path='/openstack/latest/user_data.json')
            iso.add_fp(io.BytesIO(user_data),
                       len(user_data),
                       '/openstack/2017-02-22/user_data',
                       rr_name='user_data',
                       joliet_path='/openstack/2017-02-22/user_data.json')

        # network_data.json
        nd = {
            'links': [],
            'networks': [],
            'services': [{
                'address': '8.8.8.8',
                'type': 'dns'
            }]
        }

        seen_networks = []
        for iface in db.get_instance_interfaces(self.db_entry['uuid']):
            devname = 'eth%d' % iface['order']
            nd['links'].append({
                'ethernet_mac_address': iface['macaddr'],
                'id': devname,
                'name': devname,
                'mtu': 1450,
                'type': 'vif',
                'vif_id': iface['uuid']
            })

            if not iface['network_uuid'] in seen_networks:
                n = net.from_db(iface['network_uuid'])
                nd['networks'].append({
                    'id':
                    iface['network_uuid'],
                    'link':
                    devname,
                    'type':
                    'ipv4',
                    'ip_address':
                    iface['ipv4'],
                    'netmask':
                    str(n.netmask),
                    'routes': [{
                        'network': '0.0.0.0',
                        'netmask': '0.0.0.0',
                        'gateway': str(n.router)
                    }],
                    'network_id':
                    iface['network_uuid']
                })
                seen_networks.append(iface['network_uuid'])

        nd_encoded = json.dumps(nd).encode('ascii')
        iso.add_fp(io.BytesIO(nd_encoded),
                   len(nd_encoded),
                   '/openstack/latest/network_data.json;3',
                   rr_name='network_data.json',
                   joliet_path='/openstack/latest/vendor_data.json')
        iso.add_fp(io.BytesIO(nd_encoded),
                   len(nd_encoded),
                   '/openstack/2017-02-22/network_data.json;4',
                   rr_name='network_data.json',
                   joliet_path='/openstack/2017-02-22/vendor_data.json')

        # empty vendor_data.json and vendor_data2.json
        vd = '{}'.encode('ascii')
        iso.add_fp(io.BytesIO(vd),
                   len(vd),
                   '/openstack/latest/vendor_data.json;5',
                   rr_name='vendor_data.json',
                   joliet_path='/openstack/latest/vendor_data.json')
        iso.add_fp(io.BytesIO(vd),
                   len(vd),
                   '/openstack/2017-02-22/vendor_data.json;6',
                   rr_name='vendor_data.json',
                   joliet_path='/openstack/2017-02-22/vendor_data.json')
        iso.add_fp(io.BytesIO(vd),
                   len(vd),
                   '/openstack/latest/vendor_data2.json;7',
                   rr_name='vendor_data2.json',
                   joliet_path='/openstack/latest/vendor_data2.json')
        iso.add_fp(io.BytesIO(vd),
                   len(vd),
                   '/openstack/2017-02-22/vendor_data2.json;8',
                   rr_name='vendor_data2.json',
                   joliet_path='/openstack/2017-02-22/vendor_data2.json')

        # Dump to disk
        iso.write(disk_path)
        iso.close()
예제 #18
0
    def post(self,
             name=None,
             cpus=None,
             memory=None,
             network=None,
             disk=None,
             ssh_key=None,
             user_data=None,
             placed_on=None,
             namespace=None,
             instance_uuid=None):
        global SCHEDULER

        # We need to sanitise the name so its safe for DNS
        name = re.sub(r'([^a-zA-Z0-9_\-])', '', name)

        if not namespace:
            namespace = get_jwt_identity()

        # If accessing a foreign namespace, we need to be an admin
        if get_jwt_identity() not in [namespace, 'system']:
            return error(
                401,
                'only admins can create resources in a different namespace')

        # The instance needs to exist in the DB before network interfaces are created
        if not instance_uuid:
            instance_uuid = str(uuid.uuid4())
            db.add_event('instance', instance_uuid, 'uuid allocated', None,
                         None, None)

        # Create instance object
        instance = virt.from_db(instance_uuid)
        if instance:
            if get_jwt_identity() not in [
                    instance.db_entry['namespace'], 'system'
            ]:
                LOG.info('instance(%s): instance not found, ownership test' %
                         instance_uuid)
                return error(404, 'instance not found')

        if not instance:
            instance = virt.from_definition(uuid=instance_uuid,
                                            name=name,
                                            disks=disk,
                                            memory_mb=memory,
                                            vcpus=cpus,
                                            ssh_key=ssh_key,
                                            user_data=user_data,
                                            owner=namespace)

        if not SCHEDULER:
            SCHEDULER = scheduler.Scheduler()

        # Have we been placed?
        if not placed_on:
            candidates = SCHEDULER.place_instance(instance, network)
            if len(candidates) == 0:
                db.add_event('instance', instance_uuid, 'schedule', 'failed',
                             None, 'insufficient resources')
                db.update_instance_state(instance_uuid, 'error')
                return error(507, 'insufficient capacity')

            placed_on = candidates[0]
            db.place_instance(instance_uuid, placed_on)
            db.add_event('instance', instance_uuid, 'placement', None, None,
                         placed_on)

        else:
            try:
                candidates = SCHEDULER.place_instance(instance,
                                                      network,
                                                      candidates=[placed_on])
                if len(candidates) == 0:
                    db.add_event('instance', instance_uuid, 'schedule',
                                 'failed', None, 'insufficient resources')
                    db.update_instance_state(instance_uuid, 'error')
                    return error(507, 'insufficient capacity')
            except scheduler.CandidateNodeNotFoundException as e:
                return error(404, 'node not found: %s' % e)

        # Have we been placed on a different node?
        if not placed_on == config.parsed.get('NODE_NAME'):
            body = flask_get_post_body()
            body['placed_on'] = placed_on
            body['instance_uuid'] = instance_uuid
            body['namespace'] = namespace

            token = util.get_api_token(
                'http://%s:%d' % (placed_on, config.parsed.get('API_PORT')),
                namespace=namespace)
            r = requests.request('POST',
                                 'http://%s:%d/instances' %
                                 (placed_on, config.parsed.get('API_PORT')),
                                 data=json.dumps(body),
                                 headers={
                                     'Authorization': token,
                                     'User-Agent': util.get_user_agent()
                                 })

            LOG.info('Returning proxied request: %d, %s' %
                     (r.status_code, r.text))
            resp = flask.Response(r.text, mimetype='application/json')
            resp.status_code = r.status_code
            return resp

        # Check we can get the required IPs
        nets = {}
        allocations = {}

        def error_with_cleanup(status_code, message):
            for network_uuid in allocations:
                n = net.from_db(network_uuid)
                for addr, _ in allocations[network_uuid]:
                    with db.get_lock('sf/ipmanager/%s' % n.uuid, ttl=120) as _:
                        ipm = db.get_ipmanager(n.uuid)
                        ipm.release(addr)
                        db.persist_ipmanager(n.uuid, ipm.save())
            return error(status_code, message)

        order = 0
        if network:
            for netdesc in network:
                if 'network_uuid' not in netdesc or not netdesc['network_uuid']:
                    return error_with_cleanup(404, 'network not specified')

                if netdesc['network_uuid'] not in nets:
                    n = net.from_db(netdesc['network_uuid'])
                    if not n:
                        return error_with_cleanup(
                            404,
                            'network %s not found' % netdesc['network_uuid'])
                    nets[netdesc['network_uuid']] = n
                    n.create()

                with db.get_lock('sf/ipmanager/%s' % netdesc['network_uuid'],
                                 ttl=120) as _:
                    db.add_event('network', netdesc['network_uuid'],
                                 'allocate address', None, None, instance_uuid)
                    allocations.setdefault(netdesc['network_uuid'], [])
                    ipm = db.get_ipmanager(netdesc['network_uuid'])
                    if 'address' not in netdesc or not netdesc['address']:
                        netdesc['address'] = ipm.get_random_free_address()
                    else:
                        if not ipm.reserve(netdesc['address']):
                            return error_with_cleanup(
                                409, 'address %s in use' % netdesc['address'])
                    db.persist_ipmanager(netdesc['network_uuid'], ipm.save())
                    allocations[netdesc['network_uuid']].append(
                        (netdesc['address'], order))

                if 'model' not in netdesc or not netdesc['model']:
                    netdesc['model'] = 'virtio'

                db.create_network_interface(str(uuid.uuid4()), netdesc,
                                            instance_uuid, order)

                order += 1

        # Initialise metadata
        db.persist_metadata('instance', instance_uuid, {})

        # Now we can start the instance
        with db.get_lock('sf/instance/%s' % instance.db_entry['uuid'],
                         ttl=900) as lock:
            with util.RecordedOperation('ensure networks exist',
                                        instance) as _:
                for network_uuid in nets:
                    n = nets[network_uuid]
                    n.ensure_mesh()
                    n.update_dhcp()

            with util.RecordedOperation('instance creation', instance) as _:
                instance.create(lock=lock)

            for iface in db.get_instance_interfaces(instance.db_entry['uuid']):
                db.update_network_interface_state(iface['uuid'], 'created')

            return db.get_instance(instance_uuid)
예제 #19
0
 def get(self, instance_uuid=None, instance_from_db=None):
     db.add_event('instance', instance_uuid, 'api', 'get interfaces', None,
                  None)
     return list(db.get_instance_interfaces(instance_uuid))
예제 #20
0
    def _maintain_networks(self):
        LOG.info('Maintaining networks')

        # Discover what networks are present
        _, _, vxid_to_mac = util.discover_interfaces()

        # Determine what networks we should be on
        host_networks = []
        seen_vxids = []

        if not util.is_network_node():
            # For normal nodes, just the ones we have instances for
            for inst in list(db.get_instances(only_node=config.parsed.get('NODE_NAME'))):
                for iface in db.get_instance_interfaces(inst['uuid']):
                    if not iface['network_uuid'] in host_networks:
                        host_networks.append(iface['network_uuid'])
        else:
            # For network nodes, its all networks
            for n in db.get_networks():
                host_networks.append(n['uuid'])

                # Network nodes also look for interfaces for absent instances
                # and delete them
                for ni in db.get_network_interfaces(n['uuid']):
                    inst = db.get_instance(ni['instance_uuid'])
                    if (not inst
                            or inst.get('state', 'unknown') in ['deleted', 'error', 'unknown']):
                        db.hard_delete_network_interface(ni['uuid'])
                        LOG.withInstance(
                            ni['instance_uuid']).withNetworkInterface(
                            ni['uuid']).info('Hard deleted stray network interface')

        # Ensure we are on every network we have a host for
        for network in host_networks:
            try:
                n = net.from_db(network)
                if not n:
                    continue

                if n.db_entry['state_updated'] - time.time() < 60:
                    # Network state changed in the last minute, punt for now
                    continue

                if not n.is_okay():
                    LOG.withObj(n).info('Recreating not okay network')
                    n.create()

                n.ensure_mesh()
                seen_vxids.append(n.vxlan_id)

            except exceptions.LockException as e:
                LOG.warning(
                    'Failed to acquire lock while maintaining networks: %s' % e)

        # Determine if there are any extra vxids
        extra_vxids = set(vxid_to_mac.keys()) - set(seen_vxids)

        # Delete "deleted" SF networks and log unknown vxlans
        if extra_vxids:
            LOG.withField('vxids', extra_vxids).warning(
                'Extra vxlans present!')

            # Determine the network uuids for those vxids
            # vxid_to_uuid = {}
            # for n in db.get_networks():
            #     vxid_to_uuid[n['vxid']] = n['uuid']

            # for extra in extra_vxids:
            #     if extra in vxid_to_uuid:
            #         with db.get_lock('network', None, vxid_to_uuid[extra],
            #                          ttl=120, op='Network reap VXLAN'):
            #             n = net.from_db(vxid_to_uuid[extra])
            #             n.delete()
            #             LOG.info('Extra vxlan %s (network %s) removed.'
            #                      % (extra, vxid_to_uuid[extra]))
            #     else:
            #         LOG.error('Extra vxlan %s does not map to any network.'
            #                   % extra)

        # And record vxids in the database
        db.persist_node_vxid_mapping(
            config.parsed.get('NODE_NAME'), vxid_to_mac)
예제 #21
0
 def get(self, instance_uuid=None, instance_from_db=None):
     return list(db.get_instance_interfaces(instance_uuid))