示例#1
0
文件: numa.py 项目: 2Exception/patron
class NUMAPagesTopology(base.NovaObject, base.NovaObjectDictCompat):
    # Version 1.0: Initial version
    VERSION = '1.0'

    fields = {
        'size_kb': fields.IntegerField(),
        'total': fields.IntegerField(),
        'used': fields.IntegerField(default=0),
    }

    def __eq__(self, other):
        return all_things_equal(self, other)

    def __ne__(self, other):
        return not (self == other)

    @property
    def free(self):
        """Returns the number of avail pages."""
        return self.total - self.used

    @property
    def free_kb(self):
        """Returns the avail memory size in KiB."""
        return self.free * self.size_kb
示例#2
0
class SecurityGroupRule(base.NovaPersistentObject, base.NovaObject,
                        base.NovaObjectDictCompat):
    # Version 1.0: Initial version
    # Version 1.1: Added create() and set id as read_only
    VERSION = '1.1'

    fields = {
        'id': fields.IntegerField(read_only=True),
        'protocol': fields.StringField(nullable=True),
        'from_port': fields.IntegerField(nullable=True),
        'to_port': fields.IntegerField(nullable=True),
        'cidr': fields.IPNetworkField(nullable=True),
        'parent_group': fields.ObjectField('SecurityGroup', nullable=True),
        'grantee_group': fields.ObjectField('SecurityGroup', nullable=True),
        }

    obj_relationships = {
        'parent_group': [('1.0', '1.1'), ('1.1', '1.1')],
        'grantee_group': [('1.0', '1.1'), ('1.1', '1.1')],
    }

    @staticmethod
    def _from_db_subgroup(context, db_group):
        if db_group is None:
            return None
        return objects.SecurityGroup._from_db_object(
            context, objects.SecurityGroup(context), db_group)

    @staticmethod
    def _from_db_object(context, rule, db_rule, expected_attrs=None):
        if expected_attrs is None:
            expected_attrs = []
        for field in rule.fields:
            if field in expected_attrs:
                rule[field] = rule._from_db_subgroup(context, db_rule[field])
            elif field not in OPTIONAL_ATTRS:
                rule[field] = db_rule[field]
        rule._context = context
        rule.obj_reset_changes()
        return rule

    @base.remotable
    def create(self):
        if self.obj_attr_is_set('id'):
            raise exception.ObjectActionError(action='create',
                                      reason='already created')
        updates = self.obj_get_changes()
        parent_group = updates.pop('parent_group', None)
        if parent_group:
            updates['parent_group_id'] = parent_group.id
        grantee_group = updates.pop('grantee_group', None)
        if grantee_group:
            updates['group_id'] = grantee_group.id
        db_rule = db.security_group_rule_create(self._context, updates)
        self._from_db_object(self._context, self, db_rule)

    @base.remotable_classmethod
    def get_by_id(cls, context, rule_id):
        db_rule = db.security_group_rule_get(context, rule_id)
        return cls._from_db_object(context, cls(), db_rule)
示例#3
0
class VirtualInterface(base.NovaPersistentObject, base.NovaObject,
                       base.NovaObjectDictCompat):
    # Version 1.0: Initial version
    VERSION = '1.0'

    fields = {
        'id': fields.IntegerField(),
        'address': fields.StringField(nullable=True),
        'network_id': fields.IntegerField(),
        'instance_uuid': fields.UUIDField(),
        'uuid': fields.UUIDField(),
    }

    @staticmethod
    def _from_db_object(context, vif, db_vif):
        for field in vif.fields:
            vif[field] = db_vif[field]
        vif._context = context
        vif.obj_reset_changes()
        return vif

    @base.remotable_classmethod
    def get_by_id(cls, context, vif_id):
        db_vif = db.virtual_interface_get(context, vif_id)
        if db_vif:
            return cls._from_db_object(context, cls(), db_vif)

    @base.remotable_classmethod
    def get_by_uuid(cls, context, vif_uuid):
        db_vif = db.virtual_interface_get_by_uuid(context, vif_uuid)
        if db_vif:
            return cls._from_db_object(context, cls(), db_vif)

    @base.remotable_classmethod
    def get_by_address(cls, context, address):
        db_vif = db.virtual_interface_get_by_address(context, address)
        if db_vif:
            return cls._from_db_object(context, cls(), db_vif)

    @base.remotable_classmethod
    def get_by_instance_and_network(cls, context, instance_uuid, network_id):
        db_vif = db.virtual_interface_get_by_instance_and_network(
            context, instance_uuid, network_id)
        if db_vif:
            return cls._from_db_object(context, cls(), db_vif)

    @base.remotable
    def create(self):
        if self.obj_attr_is_set('id'):
            raise exception.ObjectActionError(action='create',
                                              reason='already created')
        updates = self.obj_get_changes()
        db_vif = db.virtual_interface_create(self._context, updates)
        self._from_db_object(self._context, self, db_vif)

    @base.remotable_classmethod
    def delete_by_instance_uuid(cls, context, instance_uuid):
        db.virtual_interface_delete_by_instance(context, instance_uuid)
示例#4
0
class InstanceFault(base.NovaPersistentObject, base.NovaObject,
                    base.NovaObjectDictCompat):
    # Version 1.0: Initial version
    # Version 1.1: String attributes updated to support unicode
    # Version 1.2: Added create()
    VERSION = '1.2'

    fields = {
        'id': fields.IntegerField(),
        'instance_uuid': fields.UUIDField(),
        'code': fields.IntegerField(),
        'message': fields.StringField(nullable=True),
        'details': fields.StringField(nullable=True),
        'host': fields.StringField(nullable=True),
    }

    @staticmethod
    def _from_db_object(context, fault, db_fault):
        # NOTE(danms): These are identical right now
        for key in fault.fields:
            fault[key] = db_fault[key]
        fault._context = context
        fault.obj_reset_changes()
        return fault

    @base.remotable_classmethod
    def get_latest_for_instance(cls, context, instance_uuid):
        db_faults = db.instance_fault_get_by_instance_uuids(
            context, [instance_uuid])
        if instance_uuid in db_faults and db_faults[instance_uuid]:
            return cls._from_db_object(context, cls(),
                                       db_faults[instance_uuid][0])

    @base.remotable
    def create(self):
        if self.obj_attr_is_set('id'):
            raise exception.ObjectActionError(action='create',
                                              reason='already created')
        values = {
            'instance_uuid': self.instance_uuid,
            'code': self.code,
            'message': self.message,
            'details': self.details,
            'host': self.host,
        }
        db_fault = db.instance_fault_create(self._context, values)
        self._from_db_object(self._context, self, db_fault)
        self.obj_reset_changes()
        # Cells should only try sending a message over to patron-cells
        # if cells is enabled and we're not the API cell. Otherwise,
        # if the API cell is calling this, we could end up with
        # infinite recursion.
        if cells_opts.get_cell_type() == 'compute':
            try:
                cells_rpcapi.CellsAPI().instance_fault_create_at_top(
                    self._context, db_fault)
            except Exception:
                LOG.exception(_LE("Failed to notify cells of instance fault"))
示例#5
0
class Migration(base.NovaPersistentObject, base.NovaObject,
                base.NovaObjectDictCompat):
    # Version 1.0: Initial version
    # Version 1.1: String attributes updated to support unicode
    VERSION = '1.1'

    fields = {
        'id': fields.IntegerField(),
        'source_compute': fields.StringField(nullable=True),
        'dest_compute': fields.StringField(nullable=True),
        'source_node': fields.StringField(nullable=True),
        'dest_node': fields.StringField(nullable=True),
        'dest_host': fields.StringField(nullable=True),
        'old_instance_type_id': fields.IntegerField(nullable=True),
        'new_instance_type_id': fields.IntegerField(nullable=True),
        'instance_uuid': fields.StringField(nullable=True),
        'status': fields.StringField(nullable=True),
    }

    @staticmethod
    def _from_db_object(context, migration, db_migration):
        for key in migration.fields:
            migration[key] = db_migration[key]
        migration._context = context
        migration.obj_reset_changes()
        return migration

    @base.remotable_classmethod
    def get_by_id(cls, context, migration_id):
        db_migration = db.migration_get(context, migration_id)
        return cls._from_db_object(context, cls(), db_migration)

    @base.remotable_classmethod
    def get_by_instance_and_status(cls, context, instance_uuid, status):
        db_migration = db.migration_get_by_instance_and_status(
            context, instance_uuid, status)
        return cls._from_db_object(context, cls(), db_migration)

    @base.remotable
    def create(self):
        if self.obj_attr_is_set('id'):
            raise exception.ObjectActionError(action='create',
                                              reason='already created')
        updates = self.obj_get_changes()
        db_migration = db.migration_create(self._context, updates)
        self._from_db_object(self._context, self, db_migration)

    @base.remotable
    def save(self):
        updates = self.obj_get_changes()
        updates.pop('id', None)
        db_migration = db.migration_update(self._context, self.id, updates)
        self._from_db_object(self._context, self, db_migration)
        self.obj_reset_changes()

    @property
    def instance(self):
        return objects.Instance.get_by_uuid(self._context, self.instance_uuid)
示例#6
0
 def setUp(self):
     super(TestInteger, self).setUp()
     self.field = fields.IntegerField()
     self.coerce_good_values = [(1, 1), ('1', 1)]
     self.coerce_bad_values = ['foo', None]
     self.to_primitive_values = self.coerce_good_values[0:1]
     self.from_primitive_values = self.coerce_good_values[0:1]
示例#7
0
class BandwidthUsage(base.NovaPersistentObject, base.NovaObject,
                     base.NovaObjectDictCompat):
    # Version 1.0: Initial version
    # Version 1.1: Add use_slave to get_by_instance_uuid_and_mac
    # Version 1.2: Add update_cells to create
    VERSION = '1.2'

    fields = {
        'instance_uuid': fields.UUIDField(),
        'mac': fields.StringField(),
        'start_period': fields.DateTimeField(),
        'last_refreshed': fields.DateTimeField(),
        'bw_in': fields.IntegerField(),
        'bw_out': fields.IntegerField(),
        'last_ctr_in': fields.IntegerField(),
        'last_ctr_out': fields.IntegerField()
    }

    @staticmethod
    def _from_db_object(context, bw_usage, db_bw_usage):
        for field in bw_usage.fields:
            bw_usage[field] = db_bw_usage[field]
        bw_usage._context = context
        bw_usage.obj_reset_changes()
        return bw_usage

    @base.serialize_args
    @base.remotable_classmethod
    def get_by_instance_uuid_and_mac(cls, context, instance_uuid, mac,
                                     start_period=None, use_slave=False):
        db_bw_usage = db.bw_usage_get(context, uuid=instance_uuid,
                                      start_period=start_period, mac=mac,
                                      use_slave=use_slave)
        if db_bw_usage:
            return cls._from_db_object(context, cls(), db_bw_usage)

    @base.serialize_args
    @base.remotable
    def create(self, uuid, mac, bw_in, bw_out, last_ctr_in,
               last_ctr_out, start_period=None, last_refreshed=None,
               update_cells=True):
        db_bw_usage = db.bw_usage_update(
            self._context, uuid, mac, start_period, bw_in, bw_out,
            last_ctr_in, last_ctr_out, last_refreshed=last_refreshed,
            update_cells=update_cells)

        self._from_db_object(self._context, self, db_bw_usage)
示例#8
0
class KeyPair(base.NovaPersistentObject, base.NovaObject,
              base.NovaObjectDictCompat):
    # Version 1.0: Initial version
    # Version 1.1: String attributes updated to support unicode
    # Version 1.2: Added keypair type
    # Version 1.3: Name field is non-null
    VERSION = '1.3'

    fields = {
        'id': fields.IntegerField(),
        'name': fields.StringField(nullable=False),
        'user_id': fields.StringField(nullable=True),
        'fingerprint': fields.StringField(nullable=True),
        'public_key': fields.StringField(nullable=True),
        'type': fields.StringField(nullable=False),
    }

    def obj_make_compatible(self, primitive, target_version):
        super(KeyPair, self).obj_make_compatible(primitive, target_version)
        target_version = utils.convert_version_to_tuple(target_version)
        if target_version < (1, 2) and 'type' in primitive:
            del primitive['type']

    @staticmethod
    def _from_db_object(context, keypair, db_keypair):
        for key in keypair.fields:
            keypair[key] = db_keypair[key]
        keypair._context = context
        keypair.obj_reset_changes()
        return keypair

    @base.remotable_classmethod
    def get_by_name(cls, context, user_id, name):
        db_keypair = db.key_pair_get(context, user_id, name)
        return cls._from_db_object(context, cls(), db_keypair)

    @base.remotable_classmethod
    def destroy_by_name(cls, context, user_id, name):
        db.key_pair_destroy(context, user_id, name)

    @base.remotable
    def create(self):
        if self.obj_attr_is_set('id'):
            raise exception.ObjectActionError(action='create',
                                              reason='already created')
        updates = self.obj_get_changes()
        db_keypair = db.key_pair_create(self._context, updates)
        self._from_db_object(self._context, self, db_keypair)

    @base.remotable
    def destroy(self):
        db.key_pair_destroy(self._context, self.user_id, self.name)
示例#9
0
class SecurityGroup(base.NovaPersistentObject, base.NovaObject,
                    base.NovaObjectDictCompat):
    # Version 1.0: Initial version
    # Version 1.1: String attributes updated to support unicode
    VERSION = '1.1'

    fields = {
        'id': fields.IntegerField(),
        'name': fields.StringField(),
        'description': fields.StringField(),
        'user_id': fields.StringField(),
        'project_id': fields.StringField(),
    }

    @staticmethod
    def _from_db_object(context, secgroup, db_secgroup):
        # NOTE(danms): These are identical right now
        for field in secgroup.fields:
            secgroup[field] = db_secgroup[field]
        secgroup._context = context
        secgroup.obj_reset_changes()
        return secgroup

    @base.remotable_classmethod
    def get(cls, context, secgroup_id):
        db_secgroup = db.security_group_get(context, secgroup_id)
        return cls._from_db_object(context, cls(), db_secgroup)

    @base.remotable_classmethod
    def get_by_name(cls, context, project_id, group_name):
        db_secgroup = db.security_group_get_by_name(context, project_id,
                                                    group_name)
        return cls._from_db_object(context, cls(), db_secgroup)

    @base.remotable
    def in_use(self):
        return db.security_group_in_use(self._context, self.id)

    @base.remotable
    def save(self):
        updates = self.obj_get_changes()
        if updates:
            db_secgroup = db.security_group_update(self._context, self.id,
                                                   updates)
            self._from_db_object(self._context, self, db_secgroup)
        self.obj_reset_changes()

    @base.remotable
    def refresh(self):
        self._from_db_object(self._context, self,
                             db.security_group_get(self._context, self.id))
示例#10
0
class VirtCPUTopology(base.NovaObject, base.NovaObjectDictCompat):
    # Version 1.0: Initial version
    VERSION = '1.0'

    fields = {
        'sockets': fields.IntegerField(nullable=True, default=1),
        'cores': fields.IntegerField(nullable=True, default=1),
        'threads': fields.IntegerField(nullable=True, default=1),
    }

    # NOTE(jaypipes): for backward compatibility, the virt CPU topology
    # data is stored in the database as a nested dict.
    @classmethod
    def from_dict(cls, data):
        return cls(sockets=data.get('sockets'),
                   cores=data.get('cores'),
                   threads=data.get('threads'))

    def to_dict(self):
        return {
            'sockets': self.sockets,
            'cores': self.cores,
            'threads': self.threads
        }
示例#11
0
class Agent(base.NovaPersistentObject, base.NovaObject,
            base.NovaObjectDictCompat):
    VERSION = '1.0'

    fields = {
        'id': fields.IntegerField(read_only=True),
        'hypervisor': fields.StringField(),
        'os': fields.StringField(),
        'architecture': fields.StringField(),
        'version': fields.StringField(),
        'url': fields.StringField(),
        'md5hash': fields.StringField(),
        }

    @staticmethod
    def _from_db_object(context, agent, db_agent):
        for name in agent.fields:
            agent[name] = db_agent[name]
        agent._context = context
        agent.obj_reset_changes()
        return agent

    @base.remotable_classmethod
    def get_by_triple(cls, context, hypervisor, os, architecture):
        db_agent = db.agent_build_get_by_triple(context, hypervisor,
                                                os, architecture)
        if not db_agent:
            return None
        return cls._from_db_object(context, objects.Agent(), db_agent)

    @base.remotable
    def create(self):
        updates = self.obj_get_changes()
        if 'id' in updates:
            raise exception.ObjectActionError(action='create',
                                              reason='Already Created')
        db_agent = db.agent_build_create(self._context, updates)
        self._from_db_object(self._context, self, db_agent)

    @base.remotable
    def destroy(self):
        db.agent_build_destroy(self._context, self.id)

    @base.remotable
    def save(self):
        updates = self.obj_get_changes()
        db.agent_build_update(self._context, self.id, updates)
        self.obj_reset_changes()
示例#12
0
class EC2VolumeMapping(base.NovaPersistentObject, base.NovaObject,
                       base.NovaObjectDictCompat):
    # Version 1.0: Initial version
    VERSION = '1.0'

    fields = {
        'id': fields.IntegerField(),
        'uuid': fields.UUIDField(),
    }

    @staticmethod
    def _from_db_object(context, vmap, db_vmap):
        for field in vmap.fields:
            vmap[field] = db_vmap[field]
        vmap._context = context
        vmap.obj_reset_changes()
        return vmap

    @base.remotable
    def create(self):
        if self.obj_attr_is_set('id'):
            raise exception.ObjectActionError(action='create',
                                              reason='already created')
        db_vmap = db.ec2_volume_create(self._context, self.uuid)
        self._from_db_object(self._context, self, db_vmap)

    @base.remotable_classmethod
    def get_by_uuid(cls, context, volume_uuid):
        db_vmap = db.ec2_volume_get_by_uuid(context, volume_uuid)
        if db_vmap:
            return cls._from_db_object(context, cls(context), db_vmap)

    @base.remotable_classmethod
    def get_by_id(cls, context, ec2_id):
        db_vmap = db.ec2_volume_get_by_id(context, ec2_id)
        if db_vmap:
            return cls._from_db_object(context, cls(context), db_vmap)
示例#13
0
文件: numa.py 项目: 2Exception/patron
class NUMACell(base.NovaObject, base.NovaObjectDictCompat):
    # Version 1.0: Initial version
    # Version 1.1: Added pinned_cpus and siblings fields
    # Version 1.2: Added mempages field
    VERSION = '1.2'

    fields = {
        'id': fields.IntegerField(read_only=True),
        'cpuset': fields.SetOfIntegersField(),
        'memory': fields.IntegerField(),
        'cpu_usage': fields.IntegerField(default=0),
        'memory_usage': fields.IntegerField(default=0),
        'pinned_cpus': fields.SetOfIntegersField(),
        'siblings': fields.ListOfSetsOfIntegersField(),
        'mempages': fields.ListOfObjectsField('NUMAPagesTopology'),
    }

    obj_relationships = {'mempages': [('1.2', '1.0')]}

    def __eq__(self, other):
        return all_things_equal(self, other)

    def __ne__(self, other):
        return not (self == other)

    @property
    def free_cpus(self):
        return self.cpuset - self.pinned_cpus or set()

    @property
    def free_siblings(self):
        return [sibling_set & self.free_cpus for sibling_set in self.siblings]

    @property
    def avail_cpus(self):
        return len(self.free_cpus)

    @property
    def avail_memory(self):
        return self.memory - self.memory_usage

    def pin_cpus(self, cpus):
        if self.pinned_cpus & cpus:
            raise exception.CPUPinningInvalid(requested=list(cpus),
                                              pinned=list(self.pinned_cpus))
        self.pinned_cpus |= cpus

    def unpin_cpus(self, cpus):
        if (self.pinned_cpus & cpus) != cpus:
            raise exception.CPUPinningInvalid(requested=list(cpus),
                                              pinned=list(self.pinned_cpus))
        self.pinned_cpus -= cpus

    def _to_dict(self):
        return {
            'id': self.id,
            'cpus': hardware.format_cpu_spec(self.cpuset, allow_ranges=False),
            'mem': {
                'total': self.memory,
                'used': self.memory_usage
            },
            'cpu_usage': self.cpu_usage
        }

    @classmethod
    def _from_dict(cls, data_dict):
        cpuset = hardware.parse_cpu_spec(data_dict.get('cpus', ''))
        cpu_usage = data_dict.get('cpu_usage', 0)
        memory = data_dict.get('mem', {}).get('total', 0)
        memory_usage = data_dict.get('mem', {}).get('used', 0)
        cell_id = data_dict.get('id')
        return cls(id=cell_id,
                   cpuset=cpuset,
                   memory=memory,
                   cpu_usage=cpu_usage,
                   memory_usage=memory_usage,
                   mempages=[],
                   pinned_cpus=set([]),
                   siblings=[])

    def can_fit_hugepages(self, pagesize, memory):
        """Returns whether memory can fit into hugepages size

        :param pagesize: a page size in KibB
        :param memory: a memory size asked to fit in KiB

        :returns: whether memory can fit in hugepages
        :raises: MemoryPageSizeNotSupported if page size not supported
        """
        for pages in self.mempages:
            if pages.size_kb == pagesize:
                return (memory <= pages.free_kb
                        and (memory % pages.size_kb) == 0)
        raise exception.MemoryPageSizeNotSupported(pagesize=pagesize)
示例#14
0
class Network(obj_base.NovaPersistentObject, obj_base.NovaObject,
              obj_base.NovaObjectDictCompat):
    # Version 1.0: Initial version
    # Version 1.1: Added in_use_on_host()
    # Version 1.2: Added mtu, dhcp_server, enable_dhcp, share_address
    VERSION = '1.2'

    fields = {
        'id': fields.IntegerField(),
        'label': fields.StringField(),
        'injected': fields.BooleanField(),
        'cidr': fields.IPV4NetworkField(nullable=True),
        'cidr_v6': fields.IPV6NetworkField(nullable=True),
        'multi_host': fields.BooleanField(),
        'netmask': fields.IPV4AddressField(nullable=True),
        'gateway': fields.IPV4AddressField(nullable=True),
        'broadcast': fields.IPV4AddressField(nullable=True),
        'netmask_v6': fields.IPV6AddressField(nullable=True),
        'gateway_v6': fields.IPV6AddressField(nullable=True),
        'bridge': fields.StringField(nullable=True),
        'bridge_interface': fields.StringField(nullable=True),
        'dns1': fields.IPAddressField(nullable=True),
        'dns2': fields.IPAddressField(nullable=True),
        'vlan': fields.IntegerField(nullable=True),
        'vpn_public_address': fields.IPAddressField(nullable=True),
        'vpn_public_port': fields.IntegerField(nullable=True),
        'vpn_private_address': fields.IPAddressField(nullable=True),
        'dhcp_start': fields.IPV4AddressField(nullable=True),
        'rxtx_base': fields.IntegerField(nullable=True),
        'project_id': fields.UUIDField(nullable=True),
        'priority': fields.IntegerField(nullable=True),
        'host': fields.StringField(nullable=True),
        'uuid': fields.UUIDField(),
        'mtu': fields.IntegerField(nullable=True),
        'dhcp_server': fields.IPAddressField(nullable=True),
        'enable_dhcp': fields.BooleanField(),
        'share_address': fields.BooleanField(),
    }

    @staticmethod
    def _convert_legacy_ipv6_netmask(netmask):
        """Handle netmask_v6 possibilities from the database.

        Historically, this was stored as just an integral CIDR prefix,
        but in the future it should be stored as an actual netmask.
        Be tolerant of either here.
        """
        try:
            prefix = int(netmask)
            return netaddr.IPNetwork('1::/%i' % prefix).netmask
        except ValueError:
            pass

        try:
            return netaddr.IPNetwork(netmask).netmask
        except netaddr.AddrFormatError:
            raise ValueError(
                _('IPv6 netmask "%s" must be a netmask '
                  'or integral prefix') % netmask)

    def obj_make_compatible(self, primitive, target_version):
        target_version = utils.convert_version_to_tuple(target_version)
        if target_version < (1, 2):
            if 'mtu' in primitive:
                del primitive['mtu']
            if 'enable_dhcp' in primitive:
                del primitive['enable_dhcp']
            if 'dhcp_server' in primitive:
                del primitive['dhcp_server']
            if 'share_address' in primitive:
                del primitive['share_address']

    @staticmethod
    def _from_db_object(context, network, db_network):
        for field in network.fields:
            db_value = db_network[field]
            if field is 'netmask_v6' and db_value is not None:
                db_value = network._convert_legacy_ipv6_netmask(db_value)
            if field is 'mtu' and db_value is None:
                db_value = CONF.network_device_mtu
            if field is 'dhcp_server' and db_value is None:
                db_value = db_network['gateway']
            if field is 'share_address' and CONF.share_dhcp_address:
                db_value = CONF.share_dhcp_address

            network[field] = db_value
        network._context = context
        network.obj_reset_changes()
        return network

    @obj_base.remotable_classmethod
    def get_by_id(cls, context, network_id, project_only='allow_none'):
        db_network = db.network_get(context,
                                    network_id,
                                    project_only=project_only)
        return cls._from_db_object(context, cls(), db_network)

    @obj_base.remotable_classmethod
    def get_by_uuid(cls, context, network_uuid):
        db_network = db.network_get_by_uuid(context, network_uuid)
        return cls._from_db_object(context, cls(), db_network)

    @obj_base.remotable_classmethod
    def get_by_cidr(cls, context, cidr):
        db_network = db.network_get_by_cidr(context, cidr)
        return cls._from_db_object(context, cls(), db_network)

    @obj_base.remotable_classmethod
    def associate(cls, context, project_id, network_id=None, force=False):
        db.network_associate(context,
                             project_id,
                             network_id=network_id,
                             force=force)

    @obj_base.remotable_classmethod
    def disassociate(cls, context, network_id, host=False, project=False):
        db.network_disassociate(context, network_id, host, project)

    @obj_base.remotable_classmethod
    def in_use_on_host(cls, context, network_id, host):
        return db.network_in_use_on_host(context, network_id, host)

    def _get_primitive_changes(self):
        changes = {}
        for key, value in self.obj_get_changes().items():
            if isinstance(value, netaddr.IPAddress):
                changes[key] = str(value)
            else:
                changes[key] = value
        return changes

    @obj_base.remotable
    def create(self):
        updates = self._get_primitive_changes()
        if 'id' in updates:
            raise exception.ObjectActionError(action='create',
                                              reason='already created')
        db_network = db.network_create_safe(self._context, updates)
        self._from_db_object(self._context, self, db_network)

    @obj_base.remotable
    def destroy(self):
        db.network_delete_safe(self._context, self.id)
        self.deleted = True
        self.obj_reset_changes(['deleted'])

    @obj_base.remotable
    def save(self):
        context = self._context
        updates = self._get_primitive_changes()
        if 'netmask_v6' in updates:
            # NOTE(danms): For some reason, historical code stores the
            # IPv6 netmask as just the CIDR mask length, so convert that
            # back here before saving for now.
            updates['netmask_v6'] = netaddr.IPNetwork(
                updates['netmask_v6']).netmask
        set_host = 'host' in updates
        if set_host:
            db.network_set_host(context, self.id, updates.pop('host'))
        if updates:
            db_network = db.network_update(context, self.id, updates)
        elif set_host:
            db_network = db.network_get(context, self.id)
        else:
            db_network = None
        if db_network is not None:
            self._from_db_object(context, self, db_network)
示例#15
0
class InstanceNUMACell(base.NovaObject, base.NovaObjectDictCompat):
    # Version 1.0: Initial version
    # Version 1.1: Add pagesize field
    # Version 1.2: Add cpu_pinning_raw and topology fields
    VERSION = '1.2'

    fields = {
        'id': obj_fields.IntegerField(),
        'cpuset': obj_fields.SetOfIntegersField(),
        'memory': obj_fields.IntegerField(),
        'pagesize': obj_fields.IntegerField(nullable=True),
        'cpu_topology': obj_fields.ObjectField('VirtCPUTopology',
                                               nullable=True),
        'cpu_pinning_raw': obj_fields.DictOfIntegersField(nullable=True)
    }

    obj_relationships = {'cpu_topology': [('1.2', '1.0')]}

    cpu_pinning = obj_fields.DictProxyField('cpu_pinning_raw')

    def __init__(self, **kwargs):
        super(InstanceNUMACell, self).__init__(**kwargs)
        if 'pagesize' not in kwargs:
            self.pagesize = None
            self.obj_reset_changes(['pagesize'])
        if 'cpu_topology' not in kwargs:
            self.cpu_topology = None
            self.obj_reset_changes(['cpu_topology'])
        if 'cpu_pinning' not in kwargs:
            self.cpu_pinning = None
            self.obj_reset_changes(['cpu_pinning_raw'])

    def __len__(self):
        return len(self.cpuset)

    def _to_dict(self):
        # NOTE(sahid): Used as legacy, could be renamed in
        # _legacy_to_dict_ to the future to avoid confusing.
        return {
            'cpus': hardware.format_cpu_spec(self.cpuset, allow_ranges=False),
            'mem': {
                'total': self.memory
            },
            'id': self.id,
            'pagesize': self.pagesize
        }

    @classmethod
    def _from_dict(cls, data_dict):
        # NOTE(sahid): Used as legacy, could be renamed in
        # _legacy_from_dict_ to the future to avoid confusing.
        cpuset = hardware.parse_cpu_spec(data_dict.get('cpus', ''))
        memory = data_dict.get('mem', {}).get('total', 0)
        cell_id = data_dict.get('id')
        pagesize = data_dict.get('pagesize')
        return cls(id=cell_id, cpuset=cpuset, memory=memory, pagesize=pagesize)

    @property
    def siblings(self):
        cpu_list = sorted(list(self.cpuset))

        threads = 0
        if self.cpu_topology:
            threads = self.cpu_topology.threads
        if threads == 1:
            threads = 0

        return map(set, zip(*[iter(cpu_list)] * threads))

    @property
    def cpu_pinning_requested(self):
        return self.cpu_pinning is not None

    def pin(self, vcpu, pcpu):
        if vcpu not in self.cpuset:
            return
        pinning_dict = self.cpu_pinning or {}
        pinning_dict[vcpu] = pcpu
        self.cpu_pinning = pinning_dict

    def pin_vcpus(self, *cpu_pairs):
        for vcpu, pcpu in cpu_pairs:
            self.pin(vcpu, pcpu)
示例#16
0
class BlockDeviceMapping(base.NovaPersistentObject, base.NovaObject,
                         base.NovaObjectDictCompat):
    # Version 1.0: Initial version
    # Version 1.1: Add instance_uuid to get_by_volume_id method
    # Version 1.2: Instance version 1.14
    # Version 1.3: Instance version 1.15
    # Version 1.4: Instance version 1.16
    # Version 1.5: Instance version 1.17
    # Version 1.6: Instance version 1.18
    # Version 1.7: Add update_or_create method
    # Version 1.8: Instance version 1.19
    VERSION = '1.8'

    fields = {
        'id': fields.IntegerField(),
        'instance_uuid': fields.UUIDField(),
        'instance': fields.ObjectField('Instance', nullable=True),
        'source_type': fields.StringField(nullable=True),
        'destination_type': fields.StringField(nullable=True),
        'guest_format': fields.StringField(nullable=True),
        'device_type': fields.StringField(nullable=True),
        'disk_bus': fields.StringField(nullable=True),
        'boot_index': fields.IntegerField(nullable=True),
        'device_name': fields.StringField(nullable=True),
        'delete_on_termination': fields.BooleanField(default=False),
        'snapshot_id': fields.StringField(nullable=True),
        'volume_id': fields.StringField(nullable=True),
        'volume_size': fields.IntegerField(nullable=True),
        'image_id': fields.StringField(nullable=True),
        'no_device': fields.BooleanField(default=False),
        'connection_info': fields.StringField(nullable=True),
    }

    obj_relationships = {
        'instance': [('1.0', '1.13'), ('1.2', '1.14'), ('1.3', '1.15'),
                     ('1.4', '1.16'), ('1.5', '1.17'), ('1.6', '1.18'),
                     ('1.8', '1.19')],
    }

    @staticmethod
    def _from_db_object(context,
                        block_device_obj,
                        db_block_device,
                        expected_attrs=None):
        if expected_attrs is None:
            expected_attrs = []
        for key in block_device_obj.fields:
            if key in BLOCK_DEVICE_OPTIONAL_ATTRS:
                continue
            block_device_obj[key] = db_block_device[key]
        if 'instance' in expected_attrs:
            my_inst = objects.Instance(context)
            my_inst._from_db_object(context, my_inst,
                                    db_block_device['instance'])
            block_device_obj.instance = my_inst

        block_device_obj._context = context
        block_device_obj.obj_reset_changes()
        return block_device_obj

    def _create(self, context, update_or_create=False):
        """Create the block device record in the database.

        In case the id field is set on the object, and if the instance is set
        raise an ObjectActionError. Resets all the changes on the object.

        Returns None

        :param context: security context used for database calls
        :param update_or_create: consider existing block devices for the
                instance based on the device name and swap, and only update
                the ones that match. Normally only used when creating the
                instance for the first time.
        """
        cell_type = cells_opts.get_cell_type()
        if cell_type == 'api':
            raise exception.ObjectActionError(
                action='create',
                reason='BlockDeviceMapping cannot be '
                'created in the API cell.')

        if self.obj_attr_is_set('id'):
            raise exception.ObjectActionError(action='create',
                                              reason='already created')
        updates = self.obj_get_changes()
        if 'instance' in updates:
            raise exception.ObjectActionError(action='create',
                                              reason='instance assigned')

        cells_create = update_or_create or None
        if update_or_create:
            db_bdm = db.block_device_mapping_update_or_create(context,
                                                              updates,
                                                              legacy=False)
        else:
            db_bdm = db.block_device_mapping_create(context,
                                                    updates,
                                                    legacy=False)

        self._from_db_object(context, self, db_bdm)
        if cell_type == 'compute':
            cells_api = cells_rpcapi.CellsAPI()
            cells_api.bdm_update_or_create_at_top(context,
                                                  self,
                                                  create=cells_create)

    @base.remotable
    def create(self):
        self._create(self._context)

    @base.remotable
    def update_or_create(self):
        self._create(self._context, update_or_create=True)

    @base.remotable
    def destroy(self):
        if not self.obj_attr_is_set('id'):
            raise exception.ObjectActionError(action='destroy',
                                              reason='already destroyed')
        db.block_device_mapping_destroy(self._context, self.id)
        delattr(self, base.get_attrname('id'))

        cell_type = cells_opts.get_cell_type()
        if cell_type == 'compute':
            cells_api = cells_rpcapi.CellsAPI()
            cells_api.bdm_destroy_at_top(self._context,
                                         self.instance_uuid,
                                         device_name=self.device_name,
                                         volume_id=self.volume_id)

    @base.remotable
    def save(self):
        updates = self.obj_get_changes()
        if 'instance' in updates:
            raise exception.ObjectActionError(action='save',
                                              reason='instance changed')
        updates.pop('id', None)
        updated = db.block_device_mapping_update(self._context,
                                                 self.id,
                                                 updates,
                                                 legacy=False)
        self._from_db_object(self._context, self, updated)
        cell_type = cells_opts.get_cell_type()
        if cell_type == 'compute':
            cells_api = cells_rpcapi.CellsAPI()
            cells_api.bdm_update_or_create_at_top(self._context, self)

    @base.remotable_classmethod
    def get_by_volume_id(cls,
                         context,
                         volume_id,
                         instance_uuid=None,
                         expected_attrs=None):
        if expected_attrs is None:
            expected_attrs = []
        db_bdm = db.block_device_mapping_get_by_volume_id(
            context, volume_id, _expected_cols(expected_attrs))
        if not db_bdm:
            raise exception.VolumeBDMNotFound(volume_id=volume_id)
        # NOTE (ndipanov): Move this to the db layer into a
        # get_by_instance_and_volume_id method
        if instance_uuid and instance_uuid != db_bdm['instance_uuid']:
            raise exception.InvalidVolume(
                reason=_("Volume does not belong to the "
                         "requested instance."))
        return cls._from_db_object(context,
                                   cls(),
                                   db_bdm,
                                   expected_attrs=expected_attrs)

    @property
    def is_root(self):
        return self.boot_index == 0

    @property
    def is_volume(self):
        return self.destination_type == 'volume'

    @property
    def is_image(self):
        return self.source_type == 'image'

    def get_image_mapping(self):
        return block_device.BlockDeviceDict(self).get_image_mapping()

    def obj_load_attr(self, attrname):
        if attrname not in BLOCK_DEVICE_OPTIONAL_ATTRS:
            raise exception.ObjectActionError(
                action='obj_load_attr',
                reason='attribute %s not lazy-loadable' % attrname)
        if not self._context:
            raise exception.OrphanedObjectError(method='obj_load_attr',
                                                objtype=self.obj_name())

        LOG.debug("Lazy-loading `%(attr)s' on %(name)s uuid %(uuid)s", {
            'attr': attrname,
            'name': self.obj_name(),
            'uuid': self.uuid,
        })
        self.instance = objects.Instance.get_by_uuid(self._context,
                                                     self.instance_uuid)
        self.obj_reset_changes(fields=['instance'])
示例#17
0
class CellMapping(base.NovaTimestampObject, base.NovaObject):
    # Version 1.0: Initial version
    VERSION = '1.0'

    fields = {
        'id': fields.IntegerField(read_only=True),
        'uuid': fields.UUIDField(),
        'name': fields.StringField(nullable=True),
        'transport_url': fields.StringField(),
        'database_connection': fields.StringField(),
        }

    @staticmethod
    def _from_db_object(context, cell_mapping, db_cell_mapping):
        for key in cell_mapping.fields:
            setattr(cell_mapping, key, db_cell_mapping[key])
        cell_mapping.obj_reset_changes()
        cell_mapping._context = context
        return cell_mapping

    @staticmethod
    def _get_by_uuid_from_db(context, uuid):
        session = db_api.get_api_session()

        with session.begin():
            db_mapping = session.query(api_models.CellMapping).filter_by(
                    uuid=uuid).first()
            if not db_mapping:
                raise exception.CellMappingNotFound(uuid=uuid)

        return db_mapping

    @base.remotable_classmethod
    def get_by_uuid(cls, context, uuid):
        db_mapping = cls._get_by_uuid_from_db(context, uuid)

        return cls._from_db_object(context, cls(), db_mapping)

    @staticmethod
    def _create_in_db(context, updates):
        session = db_api.get_api_session()

        db_mapping = api_models.CellMapping()
        db_mapping.update(updates)
        db_mapping.save(session)
        return db_mapping

    @base.remotable
    def create(self):
        db_mapping = self._create_in_db(self._context, self.obj_get_changes())
        self._from_db_object(self._context, self, db_mapping)

    @staticmethod
    def _save_in_db(context, uuid, updates):
        session = db_api.get_api_session()

        with session.begin():
            db_mapping = session.query(
                    api_models.CellMapping).filter_by(uuid=uuid).first()
            if not db_mapping:
                raise exception.CellMappingNotFound(uuid=uuid)

            db_mapping.update(updates)
            session.add(db_mapping)
        return db_mapping

    @base.remotable
    def save(self):
        changes = self.obj_get_changes()
        db_mapping = self._save_in_db(self._context, self.uuid, changes)
        self._from_db_object(self._context, self, db_mapping)
        self.obj_reset_changes()

    @staticmethod
    def _destroy_in_db(context, uuid):
        session = db_api.get_api_session()

        with session.begin():
            result = session.query(api_models.CellMapping).filter_by(
                    uuid=uuid).delete()
            if not result:
                raise exception.CellMappingNotFound(uuid=uuid)

    @base.remotable
    def destroy(self):
        self._destroy_in_db(self._context, self.uuid)
示例#18
0
class Aggregate(base.NovaPersistentObject, base.NovaObject,
                base.NovaObjectDictCompat):
    # Version 1.0: Initial version
    # Version 1.1: String attributes updated to support unicode
    VERSION = '1.1'

    fields = {
        'id': fields.IntegerField(),
        'name': fields.StringField(),
        'hosts': fields.ListOfStringsField(nullable=True),
        'metadata': fields.DictOfStringsField(nullable=True),
    }

    obj_extra_fields = ['availability_zone']

    @staticmethod
    def _from_db_object(context, aggregate, db_aggregate):
        for key in aggregate.fields:
            if key == 'metadata':
                db_key = 'metadetails'
            else:
                db_key = key
            aggregate[key] = db_aggregate[db_key]
        aggregate._context = context
        aggregate.obj_reset_changes()
        return aggregate

    def _assert_no_hosts(self, action):
        if 'hosts' in self.obj_what_changed():
            raise exception.ObjectActionError(action=action,
                                              reason='hosts updated inline')

    @base.remotable_classmethod
    def get_by_id(cls, context, aggregate_id):
        db_aggregate = db.aggregate_get(context, aggregate_id)
        return cls._from_db_object(context, cls(), db_aggregate)

    @base.remotable
    def create(self):
        if self.obj_attr_is_set('id'):
            raise exception.ObjectActionError(action='create',
                                              reason='already created')
        self._assert_no_hosts('create')
        updates = self.obj_get_changes()
        payload = dict(updates)
        if 'metadata' in updates:
            # NOTE(danms): For some reason the notification format is weird
            payload['meta_data'] = payload.pop('metadata')
        compute_utils.notify_about_aggregate_update(self._context,
                                                    "create.start", payload)
        metadata = updates.pop('metadata', None)
        db_aggregate = db.aggregate_create(self._context,
                                           updates,
                                           metadata=metadata)
        self._from_db_object(self._context, self, db_aggregate)
        payload['aggregate_id'] = self.id
        compute_utils.notify_about_aggregate_update(self._context,
                                                    "create.end", payload)

    @base.remotable
    def save(self):
        self._assert_no_hosts('save')
        updates = self.obj_get_changes()

        payload = {'aggregate_id': self.id}
        if 'metadata' in updates:
            payload['meta_data'] = updates['metadata']
        compute_utils.notify_about_aggregate_update(self._context,
                                                    "updateprop.start",
                                                    payload)
        updates.pop('id', None)
        db_aggregate = db.aggregate_update(self._context, self.id, updates)
        compute_utils.notify_about_aggregate_update(self._context,
                                                    "updateprop.end", payload)
        self._from_db_object(self._context, self, db_aggregate)

    @base.remotable
    def update_metadata(self, updates):
        payload = {'aggregate_id': self.id, 'meta_data': updates}
        compute_utils.notify_about_aggregate_update(self._context,
                                                    "updatemetadata.start",
                                                    payload)
        to_add = {}
        for key, value in updates.items():
            if value is None:
                try:
                    db.aggregate_metadata_delete(self._context, self.id, key)
                except exception.AggregateMetadataNotFound:
                    pass
                try:
                    self.metadata.pop(key)
                except KeyError:
                    pass
            else:
                to_add[key] = value
                self.metadata[key] = value
        db.aggregate_metadata_add(self._context, self.id, to_add)
        compute_utils.notify_about_aggregate_update(self._context,
                                                    "updatemetadata.end",
                                                    payload)
        self.obj_reset_changes(fields=['metadata'])

    @base.remotable
    def destroy(self):
        db.aggregate_delete(self._context, self.id)

    @base.remotable
    def add_host(self, host):
        db.aggregate_host_add(self._context, self.id, host)
        if self.hosts is None:
            self.hosts = []
        self.hosts.append(host)
        self.obj_reset_changes(fields=['hosts'])

    @base.remotable
    def delete_host(self, host):
        db.aggregate_host_delete(self._context, self.id, host)
        self.hosts.remove(host)
        self.obj_reset_changes(fields=['hosts'])

    @property
    def availability_zone(self):
        return self.metadata.get('availability_zone', None)
示例#19
0
class InstanceGroup(base.NovaPersistentObject, base.NovaObject,
                    base.NovaObjectDictCompat):
    # Version 1.0: Initial version
    # Version 1.1: String attributes updated to support unicode
    # Version 1.2: Use list/dict helpers for policies, metadetails, members
    # Version 1.3: Make uuid a non-None real string
    # Version 1.4: Add add_members()
    # Version 1.5: Add get_hosts()
    # Version 1.6: Add get_by_name()
    # Version 1.7: Deprecate metadetails
    # Version 1.8: Add count_members_by_user()
    # Version 1.9: Add get_by_instance_uuid()
    VERSION = '1.9'

    fields = {
        'id': fields.IntegerField(),
        'user_id': fields.StringField(nullable=True),
        'project_id': fields.StringField(nullable=True),
        'uuid': fields.UUIDField(),
        'name': fields.StringField(nullable=True),
        'policies': fields.ListOfStringsField(nullable=True),
        'members': fields.ListOfStringsField(nullable=True),
    }

    def obj_make_compatible(self, primitive, target_version):
        target_version = utils.convert_version_to_tuple(target_version)
        if target_version < (1, 7):
            # NOTE(danms): Before 1.7, we had an always-empty
            # metadetails property
            primitive['metadetails'] = {}

    @staticmethod
    def _from_db_object(context, instance_group, db_inst):
        """Method to help with migration to objects.

        Converts a database entity to a formal object.
        """
        # Most of the field names match right now, so be quick
        for field in instance_group.fields:
            if field == 'deleted':
                instance_group.deleted = db_inst['deleted'] == db_inst['id']
            else:
                instance_group[field] = db_inst[field]

        instance_group._context = context
        instance_group.obj_reset_changes()
        return instance_group

    @base.remotable_classmethod
    def get_by_uuid(cls, context, uuid):
        db_inst = db.instance_group_get(context, uuid)
        return cls._from_db_object(context, cls(), db_inst)

    @base.remotable_classmethod
    def get_by_name(cls, context, name):
        # TODO(russellb) We need to get the group by name here.  There's no
        # db.api method for this yet.  Come back and optimize this by
        # adding a new query by name.  This is unnecessarily expensive if a
        # tenant has lots of groups.
        igs = objects.InstanceGroupList.get_by_project_id(
            context, context.project_id)
        for ig in igs:
            if ig.name == name:
                return ig

        raise exception.InstanceGroupNotFound(group_uuid=name)

    @base.remotable_classmethod
    def get_by_instance_uuid(cls, context, instance_uuid):
        db_inst = db.instance_group_get_by_instance(context, instance_uuid)
        return cls._from_db_object(context, cls(), db_inst)

    @classmethod
    def get_by_hint(cls, context, hint):
        if uuidutils.is_uuid_like(hint):
            return cls.get_by_uuid(context, hint)
        else:
            return cls.get_by_name(context, hint)

    @base.remotable
    def save(self):
        """Save updates to this instance group."""

        updates = self.obj_get_changes()
        if not updates:
            return

        payload = dict(updates)
        payload['server_group_id'] = self.uuid

        db.instance_group_update(self._context, self.uuid, updates)
        db_inst = db.instance_group_get(self._context, self.uuid)
        self._from_db_object(self._context, self, db_inst)
        compute_utils.notify_about_server_group_update(self._context, "update",
                                                       payload)

    @base.remotable
    def refresh(self):
        """Refreshes the instance group."""
        current = self.__class__.get_by_uuid(self._context, self.uuid)
        for field in self.fields:
            if self.obj_attr_is_set(field) and self[field] != current[field]:
                self[field] = current[field]
        self.obj_reset_changes()

    @base.remotable
    def create(self):
        if self.obj_attr_is_set('id'):
            raise exception.ObjectActionError(action='create',
                                              reason='already created')
        updates = self.obj_get_changes()
        payload = dict(updates)
        updates.pop('id', None)
        policies = updates.pop('policies', None)
        members = updates.pop('members', None)

        db_inst = db.instance_group_create(self._context,
                                           updates,
                                           policies=policies,
                                           members=members)
        self._from_db_object(self._context, self, db_inst)
        payload['server_group_id'] = self.uuid
        compute_utils.notify_about_server_group_update(self._context, "create",
                                                       payload)

    @base.remotable
    def destroy(self):
        payload = {'server_group_id': self.uuid}
        db.instance_group_delete(self._context, self.uuid)
        self.obj_reset_changes()
        compute_utils.notify_about_server_group_update(self._context, "delete",
                                                       payload)

    @base.remotable_classmethod
    def add_members(cls, context, group_uuid, instance_uuids):
        payload = {
            'server_group_id': group_uuid,
            'instance_uuids': instance_uuids
        }
        members = db.instance_group_members_add(context, group_uuid,
                                                instance_uuids)
        compute_utils.notify_about_server_group_update(context, "addmember",
                                                       payload)
        return list(members)

    @base.remotable
    def get_hosts(self, exclude=None):
        """Get a list of hosts for non-deleted instances in the group

        This method allows you to get a list of the hosts where instances in
        this group are currently running.  There's also an option to exclude
        certain instance UUIDs from this calculation.

        """
        filter_uuids = self.members
        if exclude:
            filter_uuids = set(filter_uuids) - set(exclude)
        filters = {'uuid': filter_uuids, 'deleted': False}
        instances = objects.InstanceList.get_by_filters(self._context,
                                                        filters=filters)
        return list(
            set([instance.host for instance in instances if instance.host]))

    @base.remotable
    def count_members_by_user(self, user_id):
        """Count the number of instances in a group belonging to a user."""
        filter_uuids = self.members
        filters = {'uuid': filter_uuids, 'user_id': user_id, 'deleted': False}
        instances = objects.InstanceList.get_by_filters(self._context,
                                                        filters=filters)
        return len(instances)
示例#20
0
class InstanceActionEvent(base.NovaPersistentObject, base.NovaObject,
                          base.NovaObjectDictCompat):
    # Version 1.0: Initial version
    # Version 1.1: event_finish_with_failure decorated with serialize_args
    VERSION = '1.1'
    fields = {
        'id': fields.IntegerField(),
        'event': fields.StringField(nullable=True),
        'action_id': fields.IntegerField(nullable=True),
        'start_time': fields.DateTimeField(nullable=True),
        'finish_time': fields.DateTimeField(nullable=True),
        'result': fields.StringField(nullable=True),
        'traceback': fields.StringField(nullable=True),
    }

    @staticmethod
    def _from_db_object(context, event, db_event):
        for field in event.fields:
            event[field] = db_event[field]
        event._context = context
        event.obj_reset_changes()
        return event

    @staticmethod
    def pack_action_event_start(context, instance_uuid, event_name):
        values = {
            'event': event_name,
            'instance_uuid': instance_uuid,
            'request_id': context.request_id,
            'start_time': timeutils.utcnow()
        }
        return values

    @staticmethod
    def pack_action_event_finish(context,
                                 instance_uuid,
                                 event_name,
                                 exc_val=None,
                                 exc_tb=None):
        values = {
            'event': event_name,
            'instance_uuid': instance_uuid,
            'request_id': context.request_id,
            'finish_time': timeutils.utcnow()
        }
        if exc_tb is None:
            values['result'] = 'Success'
        else:
            values['result'] = 'Error'
            values['message'] = exc_val
            values['traceback'] = exc_tb
        return values

    @base.remotable_classmethod
    def get_by_id(cls, context, action_id, event_id):
        db_event = db.action_event_get_by_id(context, action_id, event_id)
        return cls._from_db_object(context, cls(), db_event)

    @base.remotable_classmethod
    def event_start(cls, context, instance_uuid, event_name, want_result=True):
        values = cls.pack_action_event_start(context, instance_uuid,
                                             event_name)
        db_event = db.action_event_start(context, values)
        if want_result:
            return cls._from_db_object(context, cls(), db_event)

    @base.serialize_args
    @base.remotable_classmethod
    def event_finish_with_failure(cls,
                                  context,
                                  instance_uuid,
                                  event_name,
                                  exc_val=None,
                                  exc_tb=None,
                                  want_result=None):
        values = cls.pack_action_event_finish(context,
                                              instance_uuid,
                                              event_name,
                                              exc_val=exc_val,
                                              exc_tb=exc_tb)
        db_event = db.action_event_finish(context, values)
        if want_result:
            return cls._from_db_object(context, cls(), db_event)

    @base.remotable_classmethod
    def event_finish(cls,
                     context,
                     instance_uuid,
                     event_name,
                     want_result=True):
        return cls.event_finish_with_failure(context,
                                             instance_uuid,
                                             event_name,
                                             exc_val=None,
                                             exc_tb=None,
                                             want_result=want_result)

    @base.remotable
    def finish_with_failure(self, exc_val, exc_tb):
        values = self.pack_action_event_finish(self._context,
                                               self.instance_uuid,
                                               self.event,
                                               exc_val=exc_val,
                                               exc_tb=exc_tb)
        db_event = db.action_event_finish(self._context, values)
        self._from_db_object(self._context, self, db_event)

    @base.remotable
    def finish(self):
        self.finish_with_failure(self._context, exc_val=None, exc_tb=None)
示例#21
0
class InstanceAction(base.NovaPersistentObject, base.NovaObject,
                     base.NovaObjectDictCompat):
    # Version 1.0: Initial version
    # Version 1.1: String attributes updated to support unicode
    VERSION = '1.1'

    fields = {
        'id': fields.IntegerField(),
        'action': fields.StringField(nullable=True),
        'instance_uuid': fields.UUIDField(nullable=True),
        'request_id': fields.StringField(nullable=True),
        'user_id': fields.StringField(nullable=True),
        'project_id': fields.StringField(nullable=True),
        'start_time': fields.DateTimeField(nullable=True),
        'finish_time': fields.DateTimeField(nullable=True),
        'message': fields.StringField(nullable=True),
    }

    @staticmethod
    def _from_db_object(context, action, db_action):
        for field in action.fields:
            action[field] = db_action[field]
        action._context = context
        action.obj_reset_changes()
        return action

    @staticmethod
    def pack_action_start(context, instance_uuid, action_name):
        values = {
            'request_id': context.request_id,
            'instance_uuid': instance_uuid,
            'user_id': context.user_id,
            'project_id': context.project_id,
            'action': action_name,
            'start_time': context.timestamp
        }
        return values

    @staticmethod
    def pack_action_finish(context, instance_uuid):
        values = {
            'request_id': context.request_id,
            'instance_uuid': instance_uuid,
            'finish_time': timeutils.utcnow()
        }
        return values

    @base.remotable_classmethod
    def get_by_request_id(cls, context, instance_uuid, request_id):
        db_action = db.action_get_by_request_id(context, instance_uuid,
                                                request_id)
        if db_action:
            return cls._from_db_object(context, cls(), db_action)

    @base.remotable_classmethod
    def action_start(cls,
                     context,
                     instance_uuid,
                     action_name,
                     want_result=True):
        values = cls.pack_action_start(context, instance_uuid, action_name)
        db_action = db.action_start(context, values)
        if want_result:
            return cls._from_db_object(context, cls(), db_action)

    @base.remotable_classmethod
    def action_finish(cls, context, instance_uuid, want_result=True):
        values = cls.pack_action_finish(context, instance_uuid)
        db_action = db.action_finish(context, values)
        if want_result:
            return cls._from_db_object(context, cls(), db_action)

    @base.remotable
    def finish(self):
        values = self.pack_action_finish(self._context, self.instance_uuid)
        db_action = db.action_finish(self._context, values)
        self._from_db_object(self._context, self, db_action)
示例#22
0
class ComputeNode(base.NovaPersistentObject, base.NovaObject,
                  base.NovaObjectDictCompat):
    # Version 1.0: Initial version
    # Version 1.1: Added get_by_service_id()
    # Version 1.2: String attributes updated to support unicode
    # Version 1.3: Added stats field
    # Version 1.4: Added host ip field
    # Version 1.5: Added numa_topology field
    # Version 1.6: Added supported_hv_specs
    # Version 1.7: Added host field
    # Version 1.8: Added get_by_host_and_nodename()
    # Version 1.9: Added pci_device_pools
    # Version 1.10: Added get_first_node_by_host_for_old_compat()
    # Version 1.11: PciDevicePoolList version 1.1
    VERSION = '1.11'

    fields = {
        'id': fields.IntegerField(read_only=True),
        'service_id': fields.IntegerField(),
        'host': fields.StringField(nullable=True),
        'vcpus': fields.IntegerField(),
        'memory_mb': fields.IntegerField(),
        'local_gb': fields.IntegerField(),
        'vcpus_used': fields.IntegerField(),
        'memory_mb_used': fields.IntegerField(),
        'local_gb_used': fields.IntegerField(),
        'hypervisor_type': fields.StringField(),
        'hypervisor_version': fields.IntegerField(),
        'hypervisor_hostname': fields.StringField(nullable=True),
        'free_ram_mb': fields.IntegerField(nullable=True),
        'free_disk_gb': fields.IntegerField(nullable=True),
        'current_workload': fields.IntegerField(nullable=True),
        'running_vms': fields.IntegerField(nullable=True),
        'cpu_info': fields.StringField(nullable=True),
        'disk_available_least': fields.IntegerField(nullable=True),
        'metrics': fields.StringField(nullable=True),
        'stats': fields.DictOfNullableStringsField(nullable=True),
        'host_ip': fields.IPAddressField(nullable=True),
        'numa_topology': fields.StringField(nullable=True),
        # NOTE(pmurray): the supported_hv_specs field maps to the
        # supported_instances field in the database
        'supported_hv_specs': fields.ListOfObjectsField('HVSpec'),
        # NOTE(pmurray): the pci_device_pools field maps to the
        # pci_stats field in the database
        'pci_device_pools': fields.ObjectField('PciDevicePoolList',
                                               nullable=True),
    }

    obj_relationships = {
        'pci_device_pools': [('1.9', '1.0'), ('1.11', '1.1')],
        'supported_hv_specs': [('1.6', '1.0')],
    }

    def obj_make_compatible(self, primitive, target_version):
        super(ComputeNode, self).obj_make_compatible(primitive, target_version)
        target_version = utils.convert_version_to_tuple(target_version)
        if target_version < (1, 7) and 'host' in primitive:
            del primitive['host']
        if target_version < (1, 5) and 'numa_topology' in primitive:
            del primitive['numa_topology']
        if target_version < (1, 4) and 'host_ip' in primitive:
            del primitive['host_ip']
        if target_version < (1, 3) and 'stats' in primitive:
            # pre 1.3 version does not have a stats field
            del primitive['stats']

    @staticmethod
    def _host_from_db_object(compute, db_compute):
        if (('host' not in db_compute or db_compute['host'] is None)
                and 'service_id' in db_compute
                and db_compute['service_id'] is not None):
            # FIXME(sbauza) : Unconverted compute record, provide compatibility
            # This has to stay until we can be sure that any/all compute nodes
            # in the database have been converted to use the host field

            # Service field of ComputeNode could be deprecated in a next patch,
            # so let's use directly the Service object
            try:
                service = objects.Service.get_by_id(compute._context,
                                                    db_compute['service_id'])
            except exception.ServiceNotFound:
                compute['host'] = None
                return
            try:
                compute['host'] = service.host
            except (AttributeError, exception.OrphanedObjectError):
                # Host can be nullable in Service
                compute['host'] = None
        elif 'host' in db_compute and db_compute['host'] is not None:
            # New-style DB having host as a field
            compute['host'] = db_compute['host']
        else:
            # We assume it should not happen but in case, let's set it to None
            compute['host'] = None

    @staticmethod
    def _from_db_object(context, compute, db_compute):
        special_cases = set([
            'stats',
            'supported_hv_specs',
            'host',
            'pci_device_pools',
        ])
        fields = set(compute.fields) - special_cases
        for key in fields:
            compute[key] = db_compute[key]

        stats = db_compute['stats']
        if stats:
            compute['stats'] = jsonutils.loads(stats)

        sup_insts = db_compute.get('supported_instances')
        if sup_insts:
            hv_specs = jsonutils.loads(sup_insts)
            hv_specs = [
                objects.HVSpec.from_list(hv_spec) for hv_spec in hv_specs
            ]
            compute['supported_hv_specs'] = hv_specs

        pci_stats = db_compute.get('pci_stats')
        compute.pci_device_pools = pci_device_pool.from_pci_stats(pci_stats)
        compute._context = context

        # Make sure that we correctly set the host field depending on either
        # host column is present in the table or not
        compute._host_from_db_object(compute, db_compute)

        compute.obj_reset_changes()
        return compute

    @base.remotable_classmethod
    def get_by_id(cls, context, compute_id):
        db_compute = db.compute_node_get(context, compute_id)
        return cls._from_db_object(context, cls(), db_compute)

    @base.remotable_classmethod
    def get_by_service_id(cls, context, service_id):
        db_computes = db.compute_nodes_get_by_service_id(context, service_id)
        # NOTE(sbauza): Old version was returning an item, we need to keep this
        # behaviour for backwards compatibility
        db_compute = db_computes[0]
        return cls._from_db_object(context, cls(), db_compute)

    @base.remotable_classmethod
    def get_by_host_and_nodename(cls, context, host, nodename):
        try:
            db_compute = db.compute_node_get_by_host_and_nodename(
                context, host, nodename)
        except exception.ComputeHostNotFound:
            # FIXME(sbauza): Some old computes can still have no host record
            # We need to provide compatibility by using the old service_id
            # record.
            # We assume the compatibility as an extra penalty of one more DB
            # call but that's necessary until all nodes are upgraded.
            try:
                service = objects.Service.get_by_compute_host(context, host)
                db_computes = db.compute_nodes_get_by_service_id(
                    context, service.id)
            except exception.ServiceNotFound:
                # We need to provide the same exception upstream
                raise exception.ComputeHostNotFound(host=host)
            db_compute = None
            for compute in db_computes:
                if compute['hypervisor_hostname'] == nodename:
                    db_compute = compute
                    # We can avoid an extra call to Service object in
                    # _from_db_object
                    db_compute['host'] = service.host
                    break
            if not db_compute:
                raise exception.ComputeHostNotFound(host=host)
        return cls._from_db_object(context, cls(), db_compute)

    @base.remotable_classmethod
    def get_first_node_by_host_for_old_compat(cls,
                                              context,
                                              host,
                                              use_slave=False):
        computes = ComputeNodeList.get_all_by_host(context, host, use_slave)
        # FIXME(sbauza): Some hypervisors (VMware, Ironic) can return multiple
        # nodes per host, we should return all the nodes and modify the callers
        # instead.
        # Arbitrarily returning the first node.
        return computes[0]

    @staticmethod
    def _convert_stats_to_db_format(updates):
        stats = updates.pop('stats', None)
        if stats is not None:
            updates['stats'] = jsonutils.dumps(stats)

    @staticmethod
    def _convert_host_ip_to_db_format(updates):
        host_ip = updates.pop('host_ip', None)
        if host_ip:
            updates['host_ip'] = str(host_ip)

    @staticmethod
    def _convert_supported_instances_to_db_format(updates):
        hv_specs = updates.pop('supported_hv_specs', None)
        if hv_specs is not None:
            hv_specs = [hv_spec.to_list() for hv_spec in hv_specs]
            updates['supported_instances'] = jsonutils.dumps(hv_specs)

    @staticmethod
    def _convert_pci_stats_to_db_format(updates):
        pools = updates.pop('pci_device_pools', None)
        if pools:
            updates['pci_stats'] = jsonutils.dumps(pools.obj_to_primitive())

    @base.remotable
    def create(self):
        if self.obj_attr_is_set('id'):
            raise exception.ObjectActionError(action='create',
                                              reason='already created')
        updates = self.obj_get_changes()
        self._convert_stats_to_db_format(updates)
        self._convert_host_ip_to_db_format(updates)
        self._convert_supported_instances_to_db_format(updates)
        self._convert_pci_stats_to_db_format(updates)

        db_compute = db.compute_node_create(self._context, updates)
        self._from_db_object(self._context, self, db_compute)

    @base.remotable
    def save(self, prune_stats=False):
        # NOTE(belliott) ignore prune_stats param, no longer relevant

        updates = self.obj_get_changes()
        updates.pop('id', None)
        self._convert_stats_to_db_format(updates)
        self._convert_host_ip_to_db_format(updates)
        self._convert_supported_instances_to_db_format(updates)
        self._convert_pci_stats_to_db_format(updates)

        db_compute = db.compute_node_update(self._context, self.id, updates)
        self._from_db_object(self._context, self, db_compute)

    @base.remotable
    def destroy(self):
        db.compute_node_delete(self._context, self.id)

    @property
    def service(self):
        if not hasattr(self, '_cached_service'):
            self._cached_service = objects.Service.get_by_id(
                self._context, self.service_id)
        return self._cached_service
示例#23
0
class Service(base.NovaPersistentObject, base.NovaObject,
              base.NovaObjectDictCompat):
    # Version 1.0: Initial version
    # Version 1.1: Added compute_node nested object
    # Version 1.2: String attributes updated to support unicode
    # Version 1.3: ComputeNode version 1.5
    # Version 1.4: Added use_slave to get_by_compute_host
    # Version 1.5: ComputeNode version 1.6
    # Version 1.6: ComputeNode version 1.7
    # Version 1.7: ComputeNode version 1.8
    # Version 1.8: ComputeNode version 1.9
    # Version 1.9: ComputeNode version 1.10
    # Version 1.10: Changes behaviour of loading compute_node
    # Version 1.11: Added get_by_host_and_binary
    # Version 1.12: ComputeNode version 1.11
    VERSION = '1.12'

    fields = {
        'id': fields.IntegerField(read_only=True),
        'host': fields.StringField(nullable=True),
        'binary': fields.StringField(nullable=True),
        'topic': fields.StringField(nullable=True),
        'report_count': fields.IntegerField(),
        'disabled': fields.BooleanField(),
        'disabled_reason': fields.StringField(nullable=True),
        'availability_zone': fields.StringField(nullable=True),
        'compute_node': fields.ObjectField('ComputeNode'),
    }

    obj_relationships = {
        'compute_node': [('1.1', '1.4'), ('1.3', '1.5'), ('1.5', '1.6'),
                         ('1.7', '1.8'), ('1.8', '1.9'), ('1.9', '1.10'),
                         ('1.12', '1.11')],
    }

    def obj_make_compatible(self, primitive, target_version):
        _target_version = utils.convert_version_to_tuple(target_version)
        if _target_version < (1, 10):
            target_compute_version = self.obj_calculate_child_version(
                target_version, 'compute_node')
            # service.compute_node was not lazy-loaded, we need to provide it
            # when called
            self._do_compute_node(self._context, primitive,
                                  target_compute_version)
        super(Service, self).obj_make_compatible(primitive, target_version)

    def _do_compute_node(self, context, primitive, target_version):
        try:
            # NOTE(sbauza): Some drivers (VMware, Ironic) can have multiple
            # nodes for the same service, but for keeping same behaviour,
            # returning only the first elem of the list
            compute = objects.ComputeNodeList.get_all_by_host(
                context, primitive['host'])[0]
        except Exception:
            return
        primitive['compute_node'] = compute.obj_to_primitive(
            target_version=target_version)

    @staticmethod
    def _from_db_object(context, service, db_service):
        allow_missing = ('availability_zone', )
        for key in service.fields:
            if key in allow_missing and key not in db_service:
                continue
            if key == 'compute_node':
                #  NOTE(sbauza); We want to only lazy-load compute_node
                continue
            else:
                service[key] = db_service[key]
        service._context = context
        service.obj_reset_changes()
        return service

    def obj_load_attr(self, attrname):
        if not self._context:
            raise exception.OrphanedObjectError(method='obj_load_attr',
                                                objtype=self.obj_name())

        LOG.debug("Lazy-loading `%(attr)s' on %(name)s id %(id)s", {
            'attr': attrname,
            'name': self.obj_name(),
            'id': self.id,
        })
        if attrname != 'compute_node':
            raise exception.ObjectActionError(
                action='obj_load_attr',
                reason='attribute %s not lazy-loadable' % attrname)
        if self.binary == 'patron-compute':
            # Only n-cpu services have attached compute_node(s)
            compute_nodes = objects.ComputeNodeList.get_all_by_host(
                self._context, self.host)
        else:
            # NOTE(sbauza); Previous behaviour was raising a ServiceNotFound,
            # we keep it for backwards compatibility
            raise exception.ServiceNotFound(service_id=self.id)
        # NOTE(sbauza): Some drivers (VMware, Ironic) can have multiple nodes
        # for the same service, but for keeping same behaviour, returning only
        # the first elem of the list
        self.compute_node = compute_nodes[0]

    @base.remotable_classmethod
    def get_by_id(cls, context, service_id):
        db_service = db.service_get(context, service_id)
        return cls._from_db_object(context, cls(), db_service)

    @base.remotable_classmethod
    def get_by_host_and_topic(cls, context, host, topic):
        db_service = db.service_get_by_host_and_topic(context, host, topic)
        return cls._from_db_object(context, cls(), db_service)

    @base.remotable_classmethod
    def get_by_host_and_binary(cls, context, host, binary):
        try:
            db_service = db.service_get_by_host_and_binary(
                context, host, binary)
        except exception.HostBinaryNotFound:
            return
        return cls._from_db_object(context, cls(), db_service)

    @base.remotable_classmethod
    def get_by_compute_host(cls, context, host, use_slave=False):
        db_service = db.service_get_by_compute_host(context, host)
        return cls._from_db_object(context, cls(), db_service)

    # NOTE(ndipanov): This is deprecated and should be removed on the next
    # major version bump
    @base.remotable_classmethod
    def get_by_args(cls, context, host, binary):
        db_service = db.service_get_by_host_and_binary(context, host, binary)
        return cls._from_db_object(context, cls(), db_service)

    @base.remotable
    def create(self):
        if self.obj_attr_is_set('id'):
            raise exception.ObjectActionError(action='create',
                                              reason='already created')
        updates = self.obj_get_changes()
        db_service = db.service_create(self._context, updates)
        self._from_db_object(self._context, self, db_service)

    @base.remotable
    def save(self):
        updates = self.obj_get_changes()
        updates.pop('id', None)
        db_service = db.service_update(self._context, self.id, updates)
        self._from_db_object(self._context, self, db_service)

    @base.remotable
    def destroy(self):
        db.service_destroy(self._context, self.id)
示例#24
0
class InstanceNUMATopology(base.NovaObject, base.NovaObjectDictCompat):
    # Version 1.0: Initial version
    # Version 1.1: Takes into account pagesize
    VERSION = '1.1'

    fields = {
        # NOTE(danms): The 'id' field is no longer used and should be
        # removed in the future when convenient
        'id': obj_fields.IntegerField(),
        'instance_uuid': obj_fields.UUIDField(),
        'cells': obj_fields.ListOfObjectsField('InstanceNUMACell'),
    }

    obj_relationships = {
        'cells': [('1.0', '1.0')],
    }

    @classmethod
    def obj_from_primitive(cls, primitive):
        if 'patron_object.name' in primitive:
            obj_topology = super(InstanceNUMATopology,
                                 cls).obj_from_primitive(primitive)
        else:
            # NOTE(sahid): This compatibility code needs to stay until we can
            # guarantee that there are no cases of the old format stored in
            # the database (or forever, if we can never guarantee that).
            obj_topology = InstanceNUMATopology._from_dict(primitive)
            obj_topology.id = 0
        return obj_topology

    @classmethod
    def obj_from_db_obj(cls, instance_uuid, db_obj):
        primitive = jsonutils.loads(db_obj)
        obj_topology = cls.obj_from_primitive(primitive)

        if 'patron_object.name' not in db_obj:
            obj_topology.instance_uuid = instance_uuid
            # No benefit to store a list of changed fields
            obj_topology.obj_reset_changes()

        return obj_topology

    # TODO(ndipanov) Remove this method on the major version bump to 2.0
    @base.remotable
    def create(self):
        self._save()

    # NOTE(ndipanov): We can't rename create and want to avoid version bump
    # as this needs to be backported to stable so this is not a @remotable
    # That's OK since we only call it from inside Instance.save() which is.
    def _save(self):
        values = {'numa_topology': self._to_json()}
        db.instance_extra_update_by_uuid(self._context, self.instance_uuid,
                                         values)
        self.obj_reset_changes()

    # NOTE(ndipanov): We want to avoid version bump
    # as this needs to be backported to stable so this is not a @remotable
    # That's OK since we only call it from inside Instance.save() which is.
    @classmethod
    def delete_by_instance_uuid(cls, context, instance_uuid):
        values = {'numa_topology': None}
        db.instance_extra_update_by_uuid(context, instance_uuid, values)

    @base.remotable_classmethod
    def get_by_instance_uuid(cls, context, instance_uuid):
        db_extra = db.instance_extra_get_by_instance_uuid(
            context, instance_uuid, columns=['numa_topology'])
        if not db_extra:
            raise exception.NumaTopologyNotFound(instance_uuid=instance_uuid)

        if db_extra['numa_topology'] is None:
            return None

        return cls.obj_from_db_obj(instance_uuid, db_extra['numa_topology'])

    def _to_json(self):
        return jsonutils.dumps(self.obj_to_primitive())

    def __len__(self):
        """Defined so that boolean testing works the same as for lists."""
        return len(self.cells)

    def _to_dict(self):
        # NOTE(sahid): Used as legacy, could be renamed in _legacy_to_dict_
        # in the future to avoid confusing.
        return {'cells': [cell._to_dict() for cell in self.cells]}

    @classmethod
    def _from_dict(cls, data_dict):
        # NOTE(sahid): Used as legacy, could be renamed in _legacy_from_dict_
        # in the future to avoid confusing.
        return cls(cells=[
            InstanceNUMACell._from_dict(cell_dict)
            for cell_dict in data_dict.get('cells', [])
        ])

    @property
    def cpu_pinning_requested(self):
        return all(cell.cpu_pinning_requested for cell in self.cells)
示例#25
0
class Flavor(base.NovaPersistentObject, base.NovaObject,
             base.NovaObjectDictCompat):
    # Version 1.0: Initial version
    # Version 1.1: Added save_projects(), save_extra_specs(), removed
    #              remoteable from save()
    VERSION = '1.1'

    fields = {
        'id': fields.IntegerField(),
        'name': fields.StringField(nullable=True),
        'memory_mb': fields.IntegerField(),
        'vcpus': fields.IntegerField(),
        'root_gb': fields.IntegerField(),
        'ephemeral_gb': fields.IntegerField(),
        'flavorid': fields.StringField(),
        'swap': fields.IntegerField(),
        'rxtx_factor': fields.FloatField(nullable=True, default=1.0),
        'vcpu_weight': fields.IntegerField(nullable=True),
        'disabled': fields.BooleanField(),
        'is_public': fields.BooleanField(),
        'extra_specs': fields.DictOfStringsField(),
        'projects': fields.ListOfStringsField(),
    }

    def __init__(self, *args, **kwargs):
        super(Flavor, self).__init__(*args, **kwargs)
        self._orig_extra_specs = {}
        self._orig_projects = []

    @staticmethod
    def _from_db_object(context, flavor, db_flavor, expected_attrs=None):
        if expected_attrs is None:
            expected_attrs = []
        flavor._context = context
        for name, field in flavor.fields.items():
            if name in OPTIONAL_FIELDS:
                continue
            value = db_flavor[name]
            if isinstance(field, fields.IntegerField):
                value = value if value is not None else 0
            flavor[name] = value

        if 'extra_specs' in expected_attrs:
            flavor.extra_specs = db_flavor['extra_specs']

        if 'projects' in expected_attrs:
            flavor._load_projects()

        flavor.obj_reset_changes()
        return flavor

    @base.remotable
    def _load_projects(self):
        self.projects = [
            x['project_id'] for x in db.flavor_access_get_by_flavor_id(
                self._context, self.flavorid)
        ]
        self.obj_reset_changes(['projects'])

    def obj_load_attr(self, attrname):
        # NOTE(danms): Only projects could be lazy-loaded right now
        if attrname != 'projects':
            raise exception.ObjectActionError(action='obj_load_attr',
                                              reason='unable to load %s' %
                                              attrname)

        self._load_projects()

    def obj_reset_changes(self, fields=None):
        super(Flavor, self).obj_reset_changes(fields=fields)
        if fields is None or 'extra_specs' in fields:
            self._orig_extra_specs = (dict(self.extra_specs)
                                      if self.obj_attr_is_set('extra_specs')
                                      else {})
        if fields is None or 'projects' in fields:
            self._orig_projects = (list(self.projects)
                                   if self.obj_attr_is_set('projects') else [])

    def obj_what_changed(self):
        changes = super(Flavor, self).obj_what_changed()
        if ('extra_specs' in self
                and self.extra_specs != self._orig_extra_specs):
            changes.add('extra_specs')
        if 'projects' in self and self.projects != self._orig_projects:
            changes.add('projects')
        return changes

    @classmethod
    def _obj_from_primitive(cls, context, objver, primitive):
        self = super(Flavor, cls)._obj_from_primitive(context, objver,
                                                      primitive)
        changes = self.obj_what_changed()
        if 'extra_specs' not in changes:
            # This call left extra_specs "clean" so update our tracker
            self._orig_extra_specs = (dict(self.extra_specs)
                                      if self.obj_attr_is_set('extra_specs')
                                      else {})
        if 'projects' not in changes:
            # This call left projects "clean" so update our tracker
            self._orig_projects = (list(self.projects)
                                   if self.obj_attr_is_set('projects') else [])
        return self

    @base.remotable_classmethod
    def get_by_id(cls, context, id):
        db_flavor = db.flavor_get(context, id)
        return cls._from_db_object(context,
                                   cls(context),
                                   db_flavor,
                                   expected_attrs=['extra_specs'])

    @base.remotable_classmethod
    def get_by_name(cls, context, name):
        db_flavor = db.flavor_get_by_name(context, name)
        return cls._from_db_object(context,
                                   cls(context),
                                   db_flavor,
                                   expected_attrs=['extra_specs'])

    @base.remotable_classmethod
    def get_by_flavor_id(cls, context, flavor_id, read_deleted=None):
        db_flavor = db.flavor_get_by_flavor_id(context, flavor_id,
                                               read_deleted)
        return cls._from_db_object(context,
                                   cls(context),
                                   db_flavor,
                                   expected_attrs=['extra_specs'])

    @base.remotable
    def add_access(self, project_id):
        if 'projects' in self.obj_what_changed():
            raise exception.ObjectActionError(action='add_access',
                                              reason='projects modified')
        db.flavor_access_add(self._context, self.flavorid, project_id)
        self._load_projects()

    @base.remotable
    def remove_access(self, project_id):
        if 'projects' in self.obj_what_changed():
            raise exception.ObjectActionError(action='remove_access',
                                              reason='projects modified')
        db.flavor_access_remove(self._context, self.flavorid, project_id)
        self._load_projects()

    @base.remotable
    def create(self):
        if self.obj_attr_is_set('id'):
            raise exception.ObjectActionError(action='create',
                                              reason='already created')
        updates = self.obj_get_changes()
        expected_attrs = []
        for attr in OPTIONAL_FIELDS:
            if attr in updates:
                expected_attrs.append(attr)
        projects = updates.pop('projects', [])
        db_flavor = db.flavor_create(self._context, updates, projects=projects)
        self._from_db_object(self._context,
                             self,
                             db_flavor,
                             expected_attrs=expected_attrs)

    @base.remotable
    def save_projects(self, to_add=None, to_delete=None):
        """Add or delete projects.

        :param:to_add: A list of projects to add
        :param:to_delete: A list of projects to remove
        """

        to_add = to_add if to_add is not None else []
        to_delete = to_delete if to_delete is not None else []

        for project_id in to_add:
            db.flavor_access_add(self._context, self.flavorid, project_id)
        for project_id in to_delete:
            db.flavor_access_remove(self._context, self.flavorid, project_id)
        self.obj_reset_changes(['projects'])

    @base.remotable
    def save_extra_specs(self, to_add=None, to_delete=None):
        """Add or delete extra_specs.

        :param:to_add: A dict of new keys to add/update
        :param:to_delete: A list of keys to remove
        """

        to_add = to_add if to_add is not None else []
        to_delete = to_delete if to_delete is not None else []

        if to_add:
            db.flavor_extra_specs_update_or_create(self._context,
                                                   self.flavorid, to_add)

        for key in to_delete:
            db.flavor_extra_specs_delete(self._context, self.flavorid, key)
        self.obj_reset_changes(['extra_specs'])

    def save(self):
        updates = self.obj_get_changes()
        projects = updates.pop('projects', None)
        extra_specs = updates.pop('extra_specs', None)
        if updates:
            raise exception.ObjectActionError(
                action='save', reason='read-only fields were changed')

        if extra_specs is not None:
            deleted_keys = (set(self._orig_extra_specs.keys()) -
                            set(extra_specs.keys()))
            added_keys = self.extra_specs
        else:
            added_keys = deleted_keys = None

        if projects is not None:
            deleted_projects = set(self._orig_projects) - set(projects)
            added_projects = set(projects) - set(self._orig_projects)
        else:
            added_projects = deleted_projects = None

        # NOTE(danms): The first remotable method we call will reset
        # our of the original values for projects and extra_specs. Thus,
        # we collect the added/deleted lists for both above and /then/
        # call these methods to update them.

        if added_keys or deleted_keys:
            self.save_extra_specs(self.extra_specs, deleted_keys)

        if added_projects or deleted_projects:
            self.save_projects(added_projects, deleted_projects)

    @base.remotable
    def destroy(self):
        db.flavor_destroy(self._context, self.name)
示例#26
0
class FixedIP(obj_base.NovaPersistentObject, obj_base.NovaObject,
              obj_base.NovaObjectDictCompat):
    # Version 1.0: Initial version
    # Version 1.1: Added virtual_interface field
    # Version 1.2: Instance version 1.14
    # Version 1.3: Instance 1.15
    # Version 1.4: Added default_route field
    # Version 1.5: Added floating_ips field
    # Version 1.6: Instance 1.16
    # Version 1.7: Instance 1.17
    # Version 1.8: Instance 1.18
    # Version 1.8: Instance 1.19
    VERSION = '1.9'

    fields = {
        'id': fields.IntegerField(),
        'address': fields.IPV4AndV6AddressField(),
        'network_id': fields.IntegerField(nullable=True),
        'virtual_interface_id': fields.IntegerField(nullable=True),
        'instance_uuid': fields.UUIDField(nullable=True),
        'allocated': fields.BooleanField(),
        'leased': fields.BooleanField(),
        'reserved': fields.BooleanField(),
        'host': fields.StringField(nullable=True),
        'default_route': fields.BooleanField(),
        'instance': fields.ObjectField('Instance', nullable=True),
        'network': fields.ObjectField('Network', nullable=True),
        'virtual_interface': fields.ObjectField('VirtualInterface',
                                                nullable=True),
        # NOTE(danms): This should not ever be made lazy-loadable
        # because it would create a bit of a loop between FixedIP
        # and FloatingIP
        'floating_ips': fields.ObjectField('FloatingIPList'),
        }

    obj_relationships = {
        'instance': [('1.0', '1.13'), ('1.2', '1.14'), ('1.3', '1.15'),
                     ('1.6', '1.16'), ('1.7', '1.17'), ('1.8', '1.18'),
                     ('1.9', '1.19')],
        'network': [('1.0', '1.2')],
        'virtual_interface': [('1.1', '1.0')],
        'floating_ips': [('1.5', '1.7')],
    }

    def obj_make_compatible(self, primitive, target_version):
        super(FixedIP, self).obj_make_compatible(primitive, target_version)
        target_version = utils.convert_version_to_tuple(target_version)
        if target_version < (1, 4) and 'default_route' in primitive:
            del primitive['default_route']

    @staticmethod
    def _from_db_object(context, fixedip, db_fixedip, expected_attrs=None):
        if expected_attrs is None:
            expected_attrs = []
        for field in fixedip.fields:
            if field == 'default_route':
                # NOTE(danms): This field is only set when doing a
                # FixedIPList.get_by_network() because it's a relatively
                # special-case thing, so skip it here
                continue
            if field not in FIXED_IP_OPTIONAL_ATTRS:
                fixedip[field] = db_fixedip[field]
        # NOTE(danms): Instance could be deleted, and thus None
        if 'instance' in expected_attrs:
            fixedip.instance = objects.Instance._from_db_object(
                context,
                objects.Instance(context),
                db_fixedip['instance']) if db_fixedip['instance'] else None
        if 'network' in expected_attrs:
            fixedip.network = objects.Network._from_db_object(
                context,
                objects.Network(context),
                db_fixedip['network']) if db_fixedip['network'] else None
        if 'virtual_interface' in expected_attrs:
            db_vif = db_fixedip['virtual_interface']
            vif = objects.VirtualInterface._from_db_object(
                context,
                objects.VirtualInterface(context),
                db_fixedip['virtual_interface']) if db_vif else None
            fixedip.virtual_interface = vif
        if 'floating_ips' in expected_attrs:
            fixedip.floating_ips = obj_base.obj_make_list(
                    context, objects.FloatingIPList(context),
                    objects.FloatingIP, db_fixedip['floating_ips'])
        fixedip._context = context
        fixedip.obj_reset_changes()
        return fixedip

    @obj_base.remotable_classmethod
    def get_by_id(cls, context, id, expected_attrs=None):
        if expected_attrs is None:
            expected_attrs = []
        get_network = 'network' in expected_attrs
        db_fixedip = db.fixed_ip_get(context, id, get_network=get_network)
        return cls._from_db_object(context, cls(context), db_fixedip,
                                   expected_attrs)

    @obj_base.remotable_classmethod
    def get_by_address(cls, context, address, expected_attrs=None):
        if expected_attrs is None:
            expected_attrs = []
        db_fixedip = db.fixed_ip_get_by_address(context, str(address),
                                                columns_to_join=expected_attrs)
        return cls._from_db_object(context, cls(context), db_fixedip,
                                   expected_attrs)

    @obj_base.remotable_classmethod
    def get_by_floating_address(cls, context, address):
        db_fixedip = db.fixed_ip_get_by_floating_address(context, str(address))
        if db_fixedip is not None:
            return cls._from_db_object(context, cls(context), db_fixedip)

    @obj_base.remotable_classmethod
    def get_by_network_and_host(cls, context, network_id, host):
        db_fixedip = db.fixed_ip_get_by_network_host(context, network_id, host)
        return cls._from_db_object(context, cls(context), db_fixedip)

    @obj_base.remotable_classmethod
    def associate(cls, context, address, instance_uuid, network_id=None,
                  reserved=False):
        db_fixedip = db.fixed_ip_associate(context, address, instance_uuid,
                                           network_id=network_id,
                                           reserved=reserved)
        return cls._from_db_object(context, cls(context), db_fixedip)

    @obj_base.remotable_classmethod
    def associate_pool(cls, context, network_id, instance_uuid=None,
                       host=None):
        db_fixedip = db.fixed_ip_associate_pool(context, network_id,
                                                instance_uuid=instance_uuid,
                                                host=host)
        return cls._from_db_object(context, cls(context), db_fixedip)

    @obj_base.remotable_classmethod
    def disassociate_by_address(cls, context, address):
        db.fixed_ip_disassociate(context, address)

    @obj_base.remotable_classmethod
    def _disassociate_all_by_timeout(cls, context, host, time_str):
        time = timeutils.parse_isotime(time_str)
        return db.fixed_ip_disassociate_all_by_timeout(context, host, time)

    @classmethod
    def disassociate_all_by_timeout(cls, context, host, time):
        return cls._disassociate_all_by_timeout(context, host,
                                                timeutils.isotime(time))

    @obj_base.remotable
    def create(self):
        updates = self.obj_get_changes()
        if 'id' in updates:
            raise exception.ObjectActionError(action='create',
                                              reason='already created')
        if 'address' in updates:
            updates['address'] = str(updates['address'])
        db_fixedip = db.fixed_ip_create(self._context, updates)
        self._from_db_object(self._context, self, db_fixedip)

    @obj_base.remotable
    def save(self):
        updates = self.obj_get_changes()
        if 'address' in updates:
            raise exception.ObjectActionError(action='save',
                                              reason='address is not mutable')
        db.fixed_ip_update(self._context, str(self.address), updates)
        self.obj_reset_changes()

    @obj_base.remotable
    def disassociate(self):
        db.fixed_ip_disassociate(self._context, str(self.address))
        self.instance_uuid = None
        self.instance = None
        self.obj_reset_changes(['instance_uuid', 'instance'])