From b814f9fef3efa1bdcb7e03a9161e08721b7bc8c4 Mon Sep 17 00:00:00 2001 From: "vladimir.p" Date: Fri, 15 Jul 2011 17:56:27 -0700 Subject: VSA: first cut. merged with 1279 --- nova/db/api.py | 88 ++++++- nova/db/sqlalchemy/api.py | 291 +++++++++++++++++++++ .../migrate_repo/versions/032_add_vsa_data.py | 152 +++++++++++ nova/db/sqlalchemy/migration.py | 3 +- nova/db/sqlalchemy/models.py | 95 +++++++ 5 files changed, 627 insertions(+), 2 deletions(-) create mode 100644 nova/db/sqlalchemy/migrate_repo/versions/032_add_vsa_data.py (limited to 'nova/db') diff --git a/nova/db/api.py b/nova/db/api.py index b7c5700e5..9147f136b 100644 --- a/nova/db/api.py +++ b/nova/db/api.py @@ -49,7 +49,8 @@ flags.DEFINE_string('volume_name_template', 'volume-%08x', 'Template string to be used to generate instance names') flags.DEFINE_string('snapshot_name_template', 'snapshot-%08x', 'Template string to be used to generate snapshot names') - +flags.DEFINE_string('vsa_name_template', 'vsa-%08x', + 'Template string to be used to generate VSA names') IMPL = utils.LazyPluggable(FLAGS['db_backend'], sqlalchemy='nova.db.sqlalchemy.api') @@ -509,6 +510,13 @@ def instance_get_all_by_project(context, project_id): return IMPL.instance_get_all_by_project(context, project_id) +def instance_get_all_by_project_and_vsa(context, project_id, vsa_id): + """Get all instance spawned by a given VSA belonging to a project.""" + return IMPL.instance_get_all_by_project_and_vsa(context, + project_id, + vsa_id) + + def instance_get_all_by_host(context, host): """Get all instance belonging to a host.""" return IMPL.instance_get_all_by_host(context, host) @@ -914,6 +922,16 @@ def volume_get_all_by_project(context, project_id): return IMPL.volume_get_all_by_project(context, project_id) +def volume_get_all_assigned_to_vsa(context, vsa_id): + """Get all volumes assigned to particular VSA.""" + return IMPL.volume_get_all_assigned_to_vsa(context, vsa_id) + + +def volume_get_all_assigned_from_vsa(context, vsa_id): + """Get all volumes created from particular VSA.""" + return IMPL.volume_get_all_assigned_from_vsa(context, vsa_id) + + def volume_get_by_ec2_id(context, ec2_id): """Get a volume by ec2 id.""" return IMPL.volume_get_by_ec2_id(context, ec2_id) @@ -1422,3 +1440,71 @@ def instance_type_extra_specs_update_or_create(context, instance_type_id, key/value pairs specified in the extra specs dict argument""" IMPL.instance_type_extra_specs_update_or_create(context, instance_type_id, extra_specs) + + +#################### + + +def drive_type_create(context, values): + """Creates drive type record.""" + return IMPL.drive_type_create(context, values) + + +def drive_type_update(context, name, values): + """Updates drive type record.""" + return IMPL.drive_type_update(context, name, values) + + +def drive_type_destroy(context, name): + """Deletes drive type record.""" + return IMPL.drive_type_destroy(context, name) + + +def drive_type_get(context, drive_type_id): + """Get drive type record by id.""" + return IMPL.drive_type_get(context, drive_type_id) + + +def drive_type_get_by_name(context, name): + """Get drive type record by name.""" + return IMPL.drive_type_get_by_name(context, name) + + +def drive_type_get_all(context, visible=None): + """Returns all (or only visible) drive types.""" + return IMPL.drive_type_get_all(context, visible) + + +def vsa_create(context, values): + """Creates Virtual Storage Array record.""" + return IMPL.vsa_create(context, values) + + +def vsa_update(context, vsa_id, values): + """Updates Virtual Storage Array record.""" + return IMPL.vsa_update(context, vsa_id, values) + + +def vsa_destroy(context, vsa_id): + """Deletes Virtual Storage Array record.""" + return IMPL.vsa_destroy(context, vsa_id) + + +def vsa_get(context, vsa_id): + """Get Virtual Storage Array record by ID.""" + return IMPL.vsa_get(context, vsa_id) + + +def vsa_get_all(context): + """Get all Virtual Storage Array records.""" + return IMPL.vsa_get_all(context) + + +def vsa_get_all_by_project(context, project_id): + """Get all Virtual Storage Array records by project ID.""" + return IMPL.vsa_get_all_by_project(context, project_id) + + +def vsa_get_vc_ips_list(context, vsa_id): + """Retrieves IPs of instances associated with Virtual Storage Array.""" + return IMPL.vsa_get_vc_ips_list(context, vsa_id) diff --git a/nova/db/sqlalchemy/api.py b/nova/db/sqlalchemy/api.py index a831516a8..aa5a6e052 100644 --- a/nova/db/sqlalchemy/api.py +++ b/nova/db/sqlalchemy/api.py @@ -1217,6 +1217,35 @@ def instance_get_all_by_project(context, project_id): all() +@require_context +def instance_get_all_by_project_and_vsa(context, project_id, vsa_id): + authorize_project_context(context, project_id) + + session = get_session() + return session.query(models.Instance).\ + options(joinedload_all('fixed_ips.floating_ips')).\ + options(joinedload('security_groups')).\ + options(joinedload_all('fixed_ips.network')).\ + options(joinedload('instance_type')).\ + filter_by(project_id=project_id).\ + filter_by(vsa_id=vsa_id).\ + filter_by(deleted=can_read_deleted(context)).\ + all() + + +@require_admin_context +def instance_get_all_by_vsa(context, vsa_id): + session = get_session() + return session.query(models.Instance).\ + options(joinedload_all('fixed_ips.floating_ips')).\ + options(joinedload('security_groups')).\ + options(joinedload_all('fixed_ips.network')).\ + options(joinedload('instance_type')).\ + filter_by(vsa_id=vsa_id).\ + filter_by(deleted=can_read_deleted(context)).\ + all() + + @require_context def instance_get_all_by_reservation(context, reservation_id): session = get_session() @@ -2018,12 +2047,14 @@ def volume_get(context, volume_id, session=None): if is_admin_context(context): result = session.query(models.Volume).\ options(joinedload('instance')).\ + options(joinedload('drive_type')).\ filter_by(id=volume_id).\ filter_by(deleted=can_read_deleted(context)).\ first() elif is_user_context(context): result = session.query(models.Volume).\ options(joinedload('instance')).\ + options(joinedload('drive_type')).\ filter_by(project_id=context.project_id).\ filter_by(id=volume_id).\ filter_by(deleted=False).\ @@ -2039,6 +2070,7 @@ def volume_get_all(context): session = get_session() return session.query(models.Volume).\ options(joinedload('instance')).\ + options(joinedload('drive_type')).\ filter_by(deleted=can_read_deleted(context)).\ all() @@ -2048,6 +2080,7 @@ def volume_get_all_by_host(context, host): session = get_session() return session.query(models.Volume).\ options(joinedload('instance')).\ + options(joinedload('drive_type')).\ filter_by(host=host).\ filter_by(deleted=can_read_deleted(context)).\ all() @@ -2057,6 +2090,7 @@ def volume_get_all_by_host(context, host): def volume_get_all_by_instance(context, instance_id): session = get_session() result = session.query(models.Volume).\ + options(joinedload('drive_type')).\ filter_by(instance_id=instance_id).\ filter_by(deleted=False).\ all() @@ -2065,6 +2099,28 @@ def volume_get_all_by_instance(context, instance_id): return result +@require_admin_context +def volume_get_all_assigned_to_vsa(context, vsa_id): + session = get_session() + result = session.query(models.Volume).\ + options(joinedload('drive_type')).\ + filter_by(to_vsa_id=vsa_id).\ + filter_by(deleted=False).\ + all() + return result + + +@require_admin_context +def volume_get_all_assigned_from_vsa(context, vsa_id): + session = get_session() + result = session.query(models.Volume).\ + options(joinedload('drive_type')).\ + filter_by(from_vsa_id=vsa_id).\ + filter_by(deleted=False).\ + all() + return result + + @require_context def volume_get_all_by_project(context, project_id): authorize_project_context(context, project_id) @@ -2072,6 +2128,7 @@ def volume_get_all_by_project(context, project_id): session = get_session() return session.query(models.Volume).\ options(joinedload('instance')).\ + options(joinedload('drive_type')).\ filter_by(project_id=project_id).\ filter_by(deleted=can_read_deleted(context)).\ all() @@ -2084,6 +2141,7 @@ def volume_get_instance(context, volume_id): filter_by(id=volume_id).\ filter_by(deleted=can_read_deleted(context)).\ options(joinedload('instance')).\ + options(joinedload('drive_type')).\ first() if not result: raise exception.VolumeNotFound(volume_id=volume_id) @@ -3286,3 +3344,236 @@ def instance_type_extra_specs_update_or_create(context, instance_type_id, "deleted": 0}) spec_ref.save(session=session) return specs + + + #################### + + +@require_admin_context +def drive_type_create(context, values): + """ + Creates drive type record. + """ + try: + drive_type_ref = models.DriveTypes() + drive_type_ref.update(values) + drive_type_ref.save() + except Exception, e: + raise exception.DBError(e) + return drive_type_ref + + +@require_admin_context +def drive_type_update(context, name, values): + """ + Updates drive type record. + """ + session = get_session() + with session.begin(): + drive_type_ref = drive_type_get_by_name(context, name, session=session) + drive_type_ref.update(values) + drive_type_ref.save(session=session) + return drive_type_ref + + +@require_admin_context +def drive_type_destroy(context, name): + """ + Deletes drive type record. + """ + session = get_session() + drive_type_ref = session.query(models.DriveTypes).\ + filter_by(name=name) + records = drive_type_ref.delete() + if records == 0: + raise exception.VirtualDiskTypeNotFoundByName(name=name) + else: + return drive_type_ref + + +@require_context +def drive_type_get(context, drive_type_id, session=None): + """ + Get drive type record by id. + """ + if not session: + session = get_session() + + result = session.query(models.DriveTypes).\ + filter_by(id=drive_type_id).\ + filter_by(deleted=can_read_deleted(context)).\ + first() + if not result: + raise exception.VirtualDiskTypeNotFound(id=drive_type_id) + + return result + + +@require_context +def drive_type_get_by_name(context, name, session=None): + """ + Get drive type record by name. + """ + if not session: + session = get_session() + + result = session.query(models.DriveTypes).\ + filter_by(name=name).\ + filter_by(deleted=can_read_deleted(context)).\ + first() + if not result: + raise exception.VirtualDiskTypeNotFoundByName(name=name) + + return result + + +@require_context +def drive_type_get_all(context, visible=False): + """ + Returns all (or only visible) drive types. + """ + session = get_session() + if not visible: + drive_types = session.query(models.DriveTypes).\ + filter_by(deleted=can_read_deleted(context)).\ + order_by("name").\ + all() + else: + drive_types = session.query(models.DriveTypes).\ + filter_by(deleted=can_read_deleted(context)).\ + filter_by(visible=True).\ + order_by("name").\ + all() + return drive_types + + + #################### + + +@require_admin_context +def vsa_create(context, values): + """ + Creates Virtual Storage Array record. + """ + try: + vsa_ref = models.VirtualStorageArray() + vsa_ref.update(values) + vsa_ref.save() + except Exception, e: + raise exception.DBError(e) + return vsa_ref + + +@require_admin_context +def vsa_update(context, vsa_id, values): + """ + Updates Virtual Storage Array record. + """ + session = get_session() + with session.begin(): + vsa_ref = vsa_get(context, vsa_id, session=session) + vsa_ref.update(values) + vsa_ref.save(session=session) + return vsa_ref + + +@require_admin_context +def vsa_destroy(context, vsa_id): + """ + Deletes Virtual Storage Array record. + """ + session = get_session() + with session.begin(): + #vsa_ref = vsa_get(context, vsa_id, session=session) + #vsa_ref.delete(session=session) + session.query(models.VirtualStorageArray).\ + filter_by(id=vsa_id).\ + update({'deleted': True, + 'deleted_at': utils.utcnow(), + 'updated_at': literal_column('updated_at')}) + + +@require_context +def vsa_get(context, vsa_id, session=None): + """ + Get Virtual Storage Array record by ID. + """ + if not session: + session = get_session() + result = None + + if is_admin_context(context): + result = session.query(models.VirtualStorageArray).\ + options(joinedload('vsa_instance_type')).\ + filter_by(id=vsa_id).\ + filter_by(deleted=can_read_deleted(context)).\ + first() + elif is_user_context(context): + result = session.query(models.VirtualStorageArray).\ + options(joinedload('vsa_instance_type')).\ + filter_by(project_id=context.project_id).\ + filter_by(id=vsa_id).\ + filter_by(deleted=False).\ + first() + if not result: + raise exception.VirtualStorageArrayNotFound(id=vsa_id) + + return result + + +@require_admin_context +def vsa_get_all(context): + """ + Get all Virtual Storage Array records. + """ + session = get_session() + return session.query(models.VirtualStorageArray).\ + options(joinedload('vsa_instance_type')).\ + filter_by(deleted=can_read_deleted(context)).\ + all() + + +@require_context +def vsa_get_all_by_project(context, project_id): + """ + Get all Virtual Storage Array records by project ID. + """ + authorize_project_context(context, project_id) + + session = get_session() + return session.query(models.VirtualStorageArray).\ + options(joinedload('vsa_instance_type')).\ + filter_by(project_id=project_id).\ + filter_by(deleted=can_read_deleted(context)).\ + all() + + +@require_context +def vsa_get_vc_ips_list(context, vsa_id): + """ + Retrieves IPs of instances associated with Virtual Storage Array. + """ + result = [] + session = get_session() + vc_instances = session.query(models.Instance).\ + options(joinedload_all('fixed_ips.floating_ips')).\ + options(joinedload('security_groups')).\ + options(joinedload_all('fixed_ips.network')).\ + options(joinedload('instance_type')).\ + filter_by(vsa_id=vsa_id).\ + filter_by(deleted=False).\ + all() + for vc_instance in vc_instances: + if vc_instance['fixed_ips']: + for fixed in vc_instance['fixed_ips']: + # insert the [floating,fixed] (if exists) in the head, + # otherwise append the [none,fixed] in the tail + ip = {} + ip['fixed'] = fixed['address'] + if fixed['floating_ips']: + ip['floating'] = fixed['floating_ips'][0]['address'] + result.append(ip) + + return result + + #################### diff --git a/nova/db/sqlalchemy/migrate_repo/versions/032_add_vsa_data.py b/nova/db/sqlalchemy/migrate_repo/versions/032_add_vsa_data.py new file mode 100644 index 000000000..7fc8f955c --- /dev/null +++ b/nova/db/sqlalchemy/migrate_repo/versions/032_add_vsa_data.py @@ -0,0 +1,152 @@ +# vim: tabstop=4 shiftwidth=4 softtabstop=4 + +# Copyright (c) 2011 Zadara Storage Inc. +# All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. + +from sqlalchemy import Column, DateTime, Integer, MetaData, String, Table +from sqlalchemy import Text, Boolean, ForeignKey + +from nova import log as logging + +meta = MetaData() + +# Just for the ForeignKey and column creation to succeed, these are not the +# actual definitions of tables . +# + +instances = Table('instances', meta, + Column('id', Integer(), primary_key=True, nullable=False), + ) + +volumes = Table('volumes', meta, + Column('id', Integer(), primary_key=True, nullable=False), + ) + +vsa_id = Column('vsa_id', Integer(), nullable=True) +to_vsa_id = Column('to_vsa_id', Integer(), nullable=True) +from_vsa_id = Column('from_vsa_id', Integer(), nullable=True) +drive_type_id = Column('drive_type_id', Integer(), nullable=True) + + +# New Tables +# + +virtual_storage_arrays = Table('virtual_storage_arrays', meta, + Column('created_at', DateTime(timezone=False)), + Column('updated_at', DateTime(timezone=False)), + Column('deleted_at', DateTime(timezone=False)), + Column('deleted', Boolean(create_constraint=True, name=None)), + Column('id', Integer(), primary_key=True, nullable=False), + Column('display_name', + String(length=255, convert_unicode=False, assert_unicode=None, + unicode_error=None, _warn_on_bytestring=False)), + Column('display_description', + String(length=255, convert_unicode=False, assert_unicode=None, + unicode_error=None, _warn_on_bytestring=False)), + Column('project_id', + String(length=255, convert_unicode=False, assert_unicode=None, + unicode_error=None, _warn_on_bytestring=False)), + Column('availability_zone', + String(length=255, convert_unicode=False, assert_unicode=None, + unicode_error=None, _warn_on_bytestring=False)), + Column('instance_type_id', Integer(), nullable=False), + Column('image_ref', + String(length=255, convert_unicode=False, assert_unicode=None, + unicode_error=None, _warn_on_bytestring=False)), + Column('vc_count', Integer(), nullable=False), + Column('vol_count', Integer(), nullable=False), + Column('status', + String(length=255, convert_unicode=False, assert_unicode=None, + unicode_error=None, _warn_on_bytestring=False)), + ) + +drive_types = Table('drive_types', meta, + Column('created_at', DateTime(timezone=False)), + Column('updated_at', DateTime(timezone=False)), + Column('deleted_at', DateTime(timezone=False)), + Column('deleted', Boolean(create_constraint=True, name=None)), + Column('id', Integer(), primary_key=True, nullable=False), + Column('name', + String(length=255, convert_unicode=False, assert_unicode=None, + unicode_error=None, _warn_on_bytestring=False), + unique=True), + Column('type', + String(length=255, convert_unicode=False, assert_unicode=None, + unicode_error=None, _warn_on_bytestring=False)), + Column('size_gb', Integer(), nullable=False), + Column('rpm', + String(length=255, convert_unicode=False, assert_unicode=None, + unicode_error=None, _warn_on_bytestring=False)), + Column('capabilities', + String(length=255, convert_unicode=False, assert_unicode=None, + unicode_error=None, _warn_on_bytestring=False)), + Column('visible', Boolean(create_constraint=True, name=None)), + ) + +#vsa_disk_association = Table('vsa_disk_association', meta, +# Column('created_at', DateTime(timezone=False)), +# Column('updated_at', DateTime(timezone=False)), +# Column('deleted_at', DateTime(timezone=False)), +# Column('deleted', Boolean(create_constraint=True, name=None)), +# Column('id', Integer(), primary_key=True, nullable=False), +# Column('drive_type_id', Integer(), ForeignKey('drive_types.id')), +# Column('vsa_id', Integer(), ForeignKey('virtual_storage_arrays.id')), +# Column('disk_num', Integer(), nullable=False), +# ) + +#new_tables = (virtual_storage_arrays, drive_types, vsa_disk_association) +new_tables = (virtual_storage_arrays, drive_types) + +# +# Tables to alter +# + + +def upgrade(migrate_engine): + + from nova import context + from nova import db + from nova import flags + + FLAGS = flags.FLAGS + + # Upgrade operations go here. Don't create your own engine; + # bind migrate_engine to your metadata + meta.bind = migrate_engine + + for table in new_tables: + try: + table.create() + except Exception: + logging.info(repr(table)) + logging.exception('Exception while creating table') + raise + + instances.create_column(vsa_id) + volumes.create_column(to_vsa_id) + volumes.create_column(from_vsa_id) + volumes.create_column(drive_type_id) + + +def downgrade(migrate_engine): + meta.bind = migrate_engine + + instances.drop_column(vsa_id) + volumes.drop_column(to_vsa_id) + volumes.drop_column(from_vsa_id) + volumes.drop_column(drive_type_id) + + for table in new_tables: + table.drop() diff --git a/nova/db/sqlalchemy/migration.py b/nova/db/sqlalchemy/migration.py index d9e303599..9b64671a3 100644 --- a/nova/db/sqlalchemy/migration.py +++ b/nova/db/sqlalchemy/migration.py @@ -64,7 +64,8 @@ def db_version(): 'users', 'user_project_association', 'user_project_role_association', 'user_role_association', - 'volumes'): + 'volumes', + 'virtual_storage_arrays', 'drive_types'): assert table in meta.tables return db_version_control(1) except AssertionError: diff --git a/nova/db/sqlalchemy/models.py b/nova/db/sqlalchemy/models.py index d29d3d6f1..7f2e9d39c 100644 --- a/nova/db/sqlalchemy/models.py +++ b/nova/db/sqlalchemy/models.py @@ -247,6 +247,43 @@ class Instance(BASE, NovaBase): # assert(state in ['nostate', 'running', 'blocked', 'paused', # 'shutdown', 'shutoff', 'crashed']) + vsa_id = Column(Integer, ForeignKey('virtual_storage_arrays.id'), + nullable=True) + + +class VirtualStorageArray(BASE, NovaBase): + """ + Represents a virtual storage array supplying block storage to instances. + """ + __tablename__ = 'virtual_storage_arrays' + + id = Column(Integer, primary_key=True, autoincrement=True) + + @property + def name(self): + return FLAGS.vsa_name_template % self.id + + # User editable field for display in user-facing UIs + display_name = Column(String(255)) + display_description = Column(String(255)) + + project_id = Column(String(255)) + availability_zone = Column(String(255)) + + instance_type_id = Column(Integer, ForeignKey('instance_types.id')) + image_ref = Column(String(255)) + vc_count = Column(Integer, default=0) # number of requested VC instances + vol_count = Column(Integer, default=0) # total number of BE volumes + status = Column(String(255)) + + #admin_pass = Column(String(255)) + + #disks = relationship(VsaDiskAssociation, + # backref=backref('vsa', uselist=False), + # foreign_keys=id, + # primaryjoin='and_(VsaDiskAssociation.vsa_id == ' + # 'VirtualStorageArray.id)') + class InstanceActions(BASE, NovaBase): """Represents a guest VM's actions and results""" @@ -277,6 +314,12 @@ class InstanceTypes(BASE, NovaBase): primaryjoin='and_(Instance.instance_type_id == ' 'InstanceTypes.id)') + vsas = relationship(VirtualStorageArray, + backref=backref('vsa_instance_type', uselist=False), + foreign_keys=id, + primaryjoin='and_(VirtualStorageArray.instance_type_id' + ' == InstanceTypes.id)') + class Volume(BASE, NovaBase): """Represents a block storage device that can be attached to a vm.""" @@ -316,6 +359,57 @@ class Volume(BASE, NovaBase): provider_location = Column(String(255)) provider_auth = Column(String(255)) + to_vsa_id = Column(Integer, + ForeignKey('virtual_storage_arrays.id'), nullable=True) + from_vsa_id = Column(Integer, + ForeignKey('virtual_storage_arrays.id'), nullable=True) + drive_type_id = Column(Integer, + ForeignKey('drive_types.id'), nullable=True) + + +class DriveTypes(BASE, NovaBase): + """Represents the known drive types (storage media).""" + __tablename__ = 'drive_types' + + id = Column(Integer, primary_key=True, autoincrement=True) + + """ + @property + def name(self): + if self.capabilities: + return FLAGS.drive_type_template_long % \ + (self.type, str(self.size_gb), self.rpm, self.capabilities) + else: + return FLAGS.drive_type_template_short % \ + (self.type, str(self.size_gb), self.rpm) + """ + + name = Column(String(255), unique=True) + type = Column(String(255)) + size_gb = Column(Integer) + rpm = Column(String(255)) + capabilities = Column(String(255)) + + visible = Column(Boolean, default=True) + + volumes = relationship(Volume, + backref=backref('drive_type', uselist=False), + foreign_keys=id, + primaryjoin='and_(Volume.drive_type_id == ' + 'DriveTypes.id)') + +# +#class VsaDiskAssociation(BASE, NovaBase): +# """associates drive types with Virtual Storage Arrays.""" +# __tablename__ = 'vsa_disk_association' +# +# id = Column(Integer, primary_key=True, autoincrement=True) +# +# drive_type_id = Column(Integer, ForeignKey('drive_types.id')) +# vsa_id = Column(Integer, ForeignKey('virtual_storage_arrays.id')) +# +# disk_num = Column(Integer, nullable=False) # number of disks + class Quota(BASE, NovaBase): """Represents a single quota override for a project. @@ -785,6 +879,7 @@ def register_models(): Network, SecurityGroup, SecurityGroupIngressRule, SecurityGroupInstanceAssociation, AuthToken, User, Project, Certificate, ConsolePool, Console, Zone, + VirtualStorageArray, DriveTypes, AgentBuild, InstanceMetadata, InstanceTypeExtraSpecs, Migration) engine = create_engine(FLAGS.sql_connection, echo=False) for model in models: -- cgit From f6844960dd062154244c706283cf1916ee7194ff Mon Sep 17 00:00:00 2001 From: "vladimir.p" Date: Fri, 15 Jul 2011 18:11:13 -0700 Subject: added missing instance_get_all_by_vsa --- nova/db/api.py | 5 +++++ 1 file changed, 5 insertions(+) (limited to 'nova/db') diff --git a/nova/db/api.py b/nova/db/api.py index 9147f136b..fde229099 100644 --- a/nova/db/api.py +++ b/nova/db/api.py @@ -522,6 +522,11 @@ def instance_get_all_by_host(context, host): return IMPL.instance_get_all_by_host(context, host) +def instance_get_all_by_vsa(context, vsa_id): + """Get all instance belonging to a VSA.""" + return IMPL.instance_get_all_by_vsa(context, vsa_id) + + def instance_get_all_by_reservation(context, reservation_id): """Get all instance belonging to a reservation.""" return IMPL.instance_get_all_by_reservation(context, reservation_id) -- cgit From 9e74803d5eb8a70ba829ac0569f1cd6cd372a6f2 Mon Sep 17 00:00:00 2001 From: "vladimir.p" Date: Fri, 22 Jul 2011 15:14:29 -0700 Subject: Reverted volume driver part --- nova/db/api.py | 10 +++++----- nova/db/sqlalchemy/api.py | 19 +++++++++---------- 2 files changed, 14 insertions(+), 15 deletions(-) (limited to 'nova/db') diff --git a/nova/db/api.py b/nova/db/api.py index fde229099..a3a6d47c4 100644 --- a/nova/db/api.py +++ b/nova/db/api.py @@ -1455,14 +1455,14 @@ def drive_type_create(context, values): return IMPL.drive_type_create(context, values) -def drive_type_update(context, name, values): +def drive_type_update(context, drive_type_id, values): """Updates drive type record.""" - return IMPL.drive_type_update(context, name, values) + return IMPL.drive_type_update(context, drive_type_id, values) -def drive_type_destroy(context, name): +def drive_type_destroy(context, drive_type_id): """Deletes drive type record.""" - return IMPL.drive_type_destroy(context, name) + return IMPL.drive_type_destroy(context, drive_type_id) def drive_type_get(context, drive_type_id): @@ -1475,7 +1475,7 @@ def drive_type_get_by_name(context, name): return IMPL.drive_type_get_by_name(context, name) -def drive_type_get_all(context, visible=None): +def drive_type_get_all(context, visible): """Returns all (or only visible) drive types.""" return IMPL.drive_type_get_all(context, visible) diff --git a/nova/db/sqlalchemy/api.py b/nova/db/sqlalchemy/api.py index aa5a6e052..c08524265 100644 --- a/nova/db/sqlalchemy/api.py +++ b/nova/db/sqlalchemy/api.py @@ -3364,31 +3364,30 @@ def drive_type_create(context, values): @require_admin_context -def drive_type_update(context, name, values): +def drive_type_update(context, drive_type_id, values): """ Updates drive type record. """ session = get_session() with session.begin(): - drive_type_ref = drive_type_get_by_name(context, name, session=session) + drive_type_ref = drive_type_get(context, drive_type_id, + session=session) drive_type_ref.update(values) drive_type_ref.save(session=session) return drive_type_ref @require_admin_context -def drive_type_destroy(context, name): +def drive_type_destroy(context, drive_type_id): """ Deletes drive type record. """ session = get_session() drive_type_ref = session.query(models.DriveTypes).\ - filter_by(name=name) + filter_by(id=drive_type_id) records = drive_type_ref.delete() if records == 0: - raise exception.VirtualDiskTypeNotFoundByName(name=name) - else: - return drive_type_ref + raise exception.VirtualDiskTypeNotFound(id=drive_type_id) @require_context @@ -3428,20 +3427,20 @@ def drive_type_get_by_name(context, name, session=None): @require_context -def drive_type_get_all(context, visible=False): +def drive_type_get_all(context, visible): """ Returns all (or only visible) drive types. """ session = get_session() - if not visible: + if visible: drive_types = session.query(models.DriveTypes).\ filter_by(deleted=can_read_deleted(context)).\ + filter_by(visible=True).\ order_by("name").\ all() else: drive_types = session.query(models.DriveTypes).\ filter_by(deleted=can_read_deleted(context)).\ - filter_by(visible=True).\ order_by("name").\ all() return drive_types -- cgit From d963e25906b75a48c75b6e589deb2a53f75d6ee3 Mon Sep 17 00:00:00 2001 From: Christopher MacGown Date: Fri, 22 Jul 2011 20:29:37 -0700 Subject: Config-Drive happiness, minus smoketest --- .../versions/035_add_config_drive_to_instances.py | 43 ++++++++++++++++++++++ nova/db/sqlalchemy/models.py | 1 + 2 files changed, 44 insertions(+) create mode 100644 nova/db/sqlalchemy/migrate_repo/versions/035_add_config_drive_to_instances.py (limited to 'nova/db') diff --git a/nova/db/sqlalchemy/migrate_repo/versions/035_add_config_drive_to_instances.py b/nova/db/sqlalchemy/migrate_repo/versions/035_add_config_drive_to_instances.py new file mode 100644 index 000000000..65ea012dd --- /dev/null +++ b/nova/db/sqlalchemy/migrate_repo/versions/035_add_config_drive_to_instances.py @@ -0,0 +1,43 @@ +# vim: tabstop=4 shiftwidth=4 softtabstop=4 + +# Copyright 2011 OpenStack LLC. +# +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. + +from sqlalchemy import Column, Integer, MetaData, String, Table + +from nova import utils + + +meta = MetaData() + +instances = Table("instances", meta, + Column("id", Integer(), primary_key=True, nullable=False)) +config_drive_column = Column("config_drive", String(255)) # matches image_ref + + +def upgrade(migrate_engine): + meta.bind = migrate_engine + instances.create_column(config_drive_column) + + rows = migrate_engine.execute(instances.select()) + for row in rows: + instance_config_drive = None # pre-existing instances don't have one. + migrate_engine.execute(instances.update()\ + .where(instances.c.id == row[0])\ + .values(config_drive=instance_config_drive)) + + +def downgrade(migrate_engine): + meta.bind = migrate_engine + instances.drop_column(config_drive_column) diff --git a/nova/db/sqlalchemy/models.py b/nova/db/sqlalchemy/models.py index c1150f7ca..73ad1f011 100644 --- a/nova/db/sqlalchemy/models.py +++ b/nova/db/sqlalchemy/models.py @@ -238,6 +238,7 @@ class Instance(BASE, NovaBase): uuid = Column(String(36)) root_device_name = Column(String(255)) + config_drive = Column(String(255)) # TODO(vish): see Ewan's email about state improvements, probably # should be in a driver base class or some such -- cgit From c500eac4589e9cb22e5e71b900164a151290ec03 Mon Sep 17 00:00:00 2001 From: "vladimir.p" Date: Mon, 25 Jul 2011 16:26:23 -0700 Subject: some cleanup. VSA flag status changes. returned some files --- nova/db/sqlalchemy/migrate_repo/versions/036_add_vsa_data.py | 1 + 1 file changed, 1 insertion(+) (limited to 'nova/db') diff --git a/nova/db/sqlalchemy/migrate_repo/versions/036_add_vsa_data.py b/nova/db/sqlalchemy/migrate_repo/versions/036_add_vsa_data.py index 7fc8f955c..5d2e56a7e 100644 --- a/nova/db/sqlalchemy/migrate_repo/versions/036_add_vsa_data.py +++ b/nova/db/sqlalchemy/migrate_repo/versions/036_add_vsa_data.py @@ -1,6 +1,7 @@ # vim: tabstop=4 shiftwidth=4 softtabstop=4 # Copyright (c) 2011 Zadara Storage Inc. +# Copyright (c) 2011 OpenStack LLC. # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may -- cgit From a0a3f0157d6f4e8563a5a1e4ee1bde92388f25fc Mon Sep 17 00:00:00 2001 From: "vladimir.p" Date: Mon, 25 Jul 2011 16:58:09 -0700 Subject: volume name change. some cleanup --- nova/db/sqlalchemy/models.py | 20 -------------------- 1 file changed, 20 deletions(-) (limited to 'nova/db') diff --git a/nova/db/sqlalchemy/models.py b/nova/db/sqlalchemy/models.py index fbc8e9e19..42b97867d 100644 --- a/nova/db/sqlalchemy/models.py +++ b/nova/db/sqlalchemy/models.py @@ -279,14 +279,6 @@ class VirtualStorageArray(BASE, NovaBase): vol_count = Column(Integer, default=0) # total number of BE volumes status = Column(String(255)) - #admin_pass = Column(String(255)) - - #disks = relationship(VsaDiskAssociation, - # backref=backref('vsa', uselist=False), - # foreign_keys=id, - # primaryjoin='and_(VsaDiskAssociation.vsa_id == ' - # 'VirtualStorageArray.id)') - class InstanceActions(BASE, NovaBase): """Represents a guest VM's actions and results""" @@ -401,18 +393,6 @@ class DriveTypes(BASE, NovaBase): primaryjoin='and_(Volume.drive_type_id == ' 'DriveTypes.id)') -# -#class VsaDiskAssociation(BASE, NovaBase): -# """associates drive types with Virtual Storage Arrays.""" -# __tablename__ = 'vsa_disk_association' -# -# id = Column(Integer, primary_key=True, autoincrement=True) -# -# drive_type_id = Column(Integer, ForeignKey('drive_types.id')) -# vsa_id = Column(Integer, ForeignKey('virtual_storage_arrays.id')) -# -# disk_num = Column(Integer, nullable=False) # number of disks - class Quota(BASE, NovaBase): """Represents a single quota override for a project. -- cgit From a72f2e29e2a35791a1c53f4f606948572ab52280 Mon Sep 17 00:00:00 2001 From: "vladimir.p" Date: Tue, 26 Jul 2011 13:25:34 -0700 Subject: VSA volume creation/deletion changes --- nova/db/sqlalchemy/api.py | 1 + 1 file changed, 1 insertion(+) (limited to 'nova/db') diff --git a/nova/db/sqlalchemy/api.py b/nova/db/sqlalchemy/api.py index 3b14f114a..50037e259 100644 --- a/nova/db/sqlalchemy/api.py +++ b/nova/db/sqlalchemy/api.py @@ -2205,6 +2205,7 @@ def volume_update(context, volume_id, values): volume_ref = volume_get(context, volume_id, session=session) volume_ref.update(values) volume_ref.save(session=session) + return volume_ref ################### -- cgit From b4159d95c32382d124c3f3f0a49f8ad9f2d41036 Mon Sep 17 00:00:00 2001 From: "vladimir.p" Date: Thu, 28 Jul 2011 00:27:16 -0700 Subject: some minor cosmetic work. addressed some dead code section --- nova/db/sqlalchemy/api.py | 2 -- nova/db/sqlalchemy/migrate_repo/versions/036_add_vsa_data.py | 12 ------------ 2 files changed, 14 deletions(-) (limited to 'nova/db') diff --git a/nova/db/sqlalchemy/api.py b/nova/db/sqlalchemy/api.py index e17859f69..d71d8787b 100644 --- a/nova/db/sqlalchemy/api.py +++ b/nova/db/sqlalchemy/api.py @@ -3526,8 +3526,6 @@ def vsa_destroy(context, vsa_id): """ session = get_session() with session.begin(): - #vsa_ref = vsa_get(context, vsa_id, session=session) - #vsa_ref.delete(session=session) session.query(models.VirtualStorageArray).\ filter_by(id=vsa_id).\ update({'deleted': True, diff --git a/nova/db/sqlalchemy/migrate_repo/versions/036_add_vsa_data.py b/nova/db/sqlalchemy/migrate_repo/versions/036_add_vsa_data.py index 5d2e56a7e..3b39ff493 100644 --- a/nova/db/sqlalchemy/migrate_repo/versions/036_add_vsa_data.py +++ b/nova/db/sqlalchemy/migrate_repo/versions/036_add_vsa_data.py @@ -96,18 +96,6 @@ drive_types = Table('drive_types', meta, Column('visible', Boolean(create_constraint=True, name=None)), ) -#vsa_disk_association = Table('vsa_disk_association', meta, -# Column('created_at', DateTime(timezone=False)), -# Column('updated_at', DateTime(timezone=False)), -# Column('deleted_at', DateTime(timezone=False)), -# Column('deleted', Boolean(create_constraint=True, name=None)), -# Column('id', Integer(), primary_key=True, nullable=False), -# Column('drive_type_id', Integer(), ForeignKey('drive_types.id')), -# Column('vsa_id', Integer(), ForeignKey('virtual_storage_arrays.id')), -# Column('disk_num', Integer(), nullable=False), -# ) - -#new_tables = (virtual_storage_arrays, drive_types, vsa_disk_association) new_tables = (virtual_storage_arrays, drive_types) # -- cgit From 820d28dcf09088b5878d4cd5dcb5f4765e0b4992 Mon Sep 17 00:00:00 2001 From: "vladimir.p" Date: Tue, 9 Aug 2011 18:14:41 -0700 Subject: Dropped vsa_id from instances --- nova/db/api.py | 12 --------- nova/db/sqlalchemy/api.py | 30 +--------------------- .../migrate_repo/versions/037_add_vsa_data.py | 7 ----- nova/db/sqlalchemy/models.py | 3 --- 4 files changed, 1 insertion(+), 51 deletions(-) (limited to 'nova/db') diff --git a/nova/db/api.py b/nova/db/api.py index 59baf94dd..0b6995f90 100644 --- a/nova/db/api.py +++ b/nova/db/api.py @@ -512,23 +512,11 @@ def instance_get_all_by_project(context, project_id): return IMPL.instance_get_all_by_project(context, project_id) -def instance_get_all_by_project_and_vsa(context, project_id, vsa_id): - """Get all instance spawned by a given VSA belonging to a project.""" - return IMPL.instance_get_all_by_project_and_vsa(context, - project_id, - vsa_id) - - def instance_get_all_by_host(context, host): """Get all instance belonging to a host.""" return IMPL.instance_get_all_by_host(context, host) -def instance_get_all_by_vsa(context, vsa_id): - """Get all instance belonging to a VSA.""" - return IMPL.instance_get_all_by_vsa(context, vsa_id) - - def instance_get_all_by_reservation(context, reservation_id): """Get all instances belonging to a reservation.""" return IMPL.instance_get_all_by_reservation(context, reservation_id) diff --git a/nova/db/sqlalchemy/api.py b/nova/db/sqlalchemy/api.py index ff6d756a1..bc1a3046c 100644 --- a/nova/db/sqlalchemy/api.py +++ b/nova/db/sqlalchemy/api.py @@ -1321,35 +1321,6 @@ def instance_get_all_by_project(context, project_id): all() -@require_context -def instance_get_all_by_project_and_vsa(context, project_id, vsa_id): - authorize_project_context(context, project_id) - - session = get_session() - return session.query(models.Instance).\ - options(joinedload_all('fixed_ips.floating_ips')).\ - options(joinedload('security_groups')).\ - options(joinedload_all('fixed_ips.network')).\ - options(joinedload('instance_type')).\ - filter_by(project_id=project_id).\ - filter_by(vsa_id=vsa_id).\ - filter_by(deleted=can_read_deleted(context)).\ - all() - - -@require_admin_context -def instance_get_all_by_vsa(context, vsa_id): - session = get_session() - return session.query(models.Instance).\ - options(joinedload_all('fixed_ips.floating_ips')).\ - options(joinedload('security_groups')).\ - options(joinedload_all('fixed_ips.network')).\ - options(joinedload('instance_type')).\ - filter_by(vsa_id=vsa_id).\ - filter_by(deleted=can_read_deleted(context)).\ - all() - - @require_context def instance_get_all_by_reservation(context, reservation_id): session = get_session() @@ -3748,6 +3719,7 @@ def vsa_get_vc_ips_list(context, vsa_id): """ result = [] session = get_session() + """ VP-TODO: CHANGE THIS!!! Need to perform a search based on meta-data """ vc_instances = session.query(models.Instance).\ options(joinedload_all('fixed_ips.floating_ips')).\ options(joinedload('security_groups')).\ diff --git a/nova/db/sqlalchemy/migrate_repo/versions/037_add_vsa_data.py b/nova/db/sqlalchemy/migrate_repo/versions/037_add_vsa_data.py index 3b39ff493..5a80f4e7a 100644 --- a/nova/db/sqlalchemy/migrate_repo/versions/037_add_vsa_data.py +++ b/nova/db/sqlalchemy/migrate_repo/versions/037_add_vsa_data.py @@ -27,15 +27,10 @@ meta = MetaData() # actual definitions of tables . # -instances = Table('instances', meta, - Column('id', Integer(), primary_key=True, nullable=False), - ) - volumes = Table('volumes', meta, Column('id', Integer(), primary_key=True, nullable=False), ) -vsa_id = Column('vsa_id', Integer(), nullable=True) to_vsa_id = Column('to_vsa_id', Integer(), nullable=True) from_vsa_id = Column('from_vsa_id', Integer(), nullable=True) drive_type_id = Column('drive_type_id', Integer(), nullable=True) @@ -123,7 +118,6 @@ def upgrade(migrate_engine): logging.exception('Exception while creating table') raise - instances.create_column(vsa_id) volumes.create_column(to_vsa_id) volumes.create_column(from_vsa_id) volumes.create_column(drive_type_id) @@ -132,7 +126,6 @@ def upgrade(migrate_engine): def downgrade(migrate_engine): meta.bind = migrate_engine - instances.drop_column(vsa_id) volumes.drop_column(to_vsa_id) volumes.drop_column(from_vsa_id) volumes.drop_column(drive_type_id) diff --git a/nova/db/sqlalchemy/models.py b/nova/db/sqlalchemy/models.py index f80029e97..236f148e4 100644 --- a/nova/db/sqlalchemy/models.py +++ b/nova/db/sqlalchemy/models.py @@ -243,9 +243,6 @@ class Instance(BASE, NovaBase): # assert(state in ['nostate', 'running', 'blocked', 'paused', # 'shutdown', 'shutoff', 'crashed']) - vsa_id = Column(Integer, ForeignKey('virtual_storage_arrays.id'), - nullable=True) - class VirtualStorageArray(BASE, NovaBase): """ -- cgit From 57b8f976f18b1f45de16ef8e87a6e215c009d228 Mon Sep 17 00:00:00 2001 From: "vladimir.p" Date: Thu, 11 Aug 2011 12:04:03 -0700 Subject: moved vsa_id to metadata. Added search my meta --- nova/db/sqlalchemy/api.py | 33 ++++++++++++++++++++++----------- 1 file changed, 22 insertions(+), 11 deletions(-) (limited to 'nova/db') diff --git a/nova/db/sqlalchemy/api.py b/nova/db/sqlalchemy/api.py index bc1a3046c..b77f11abb 100644 --- a/nova/db/sqlalchemy/api.py +++ b/nova/db/sqlalchemy/api.py @@ -1175,6 +1175,19 @@ def instance_get_all_by_filters(context, filters): return True return False + def _regexp_filter_by_metadata(instance, meta): + inst_metadata = [{node['key']: node['value']} \ + for node in instance['metadata']] + if isinstance(meta, list): + for node in meta: + if node not in inst_metadata: + return False + elif isinstance(meta, dict): + for k, v in meta.iteritems(): + if {k: v} not in inst_metadata: + return False + return True + def _regexp_filter_by_column(instance, filter_name, filter_re): try: v = getattr(instance, filter_name) @@ -1232,7 +1245,9 @@ def instance_get_all_by_filters(context, filters): query_prefix = _exact_match_filter(query_prefix, filter_name, filters.pop(filter_name)) - instances = query_prefix.all() + instances = query_prefix.\ + filter_by(deleted=can_read_deleted(context)).\ + all() if not instances: return [] @@ -1248,6 +1263,9 @@ def instance_get_all_by_filters(context, filters): filter_re = re.compile(str(filters[filter_name])) if filter_func: filter_l = lambda instance: filter_func(instance, filter_re) + elif filter_name == 'metadata': + filter_l = lambda instance: _regexp_filter_by_metadata(instance, + filters[filter_name]) else: filter_l = lambda instance: _regexp_filter_by_column(instance, filter_name, filter_re) @@ -3718,16 +3736,9 @@ def vsa_get_vc_ips_list(context, vsa_id): Retrieves IPs of instances associated with Virtual Storage Array. """ result = [] - session = get_session() - """ VP-TODO: CHANGE THIS!!! Need to perform a search based on meta-data """ - vc_instances = session.query(models.Instance).\ - options(joinedload_all('fixed_ips.floating_ips')).\ - options(joinedload('security_groups')).\ - options(joinedload_all('fixed_ips.network')).\ - options(joinedload('instance_type')).\ - filter_by(vsa_id=vsa_id).\ - filter_by(deleted=False).\ - all() + + vc_instances = instance_get_all_by_filters(context, + search_opts={'metadata': dict(vsa_id=str(vsa_id))}) for vc_instance in vc_instances: if vc_instance['fixed_ips']: for fixed in vc_instance['fixed_ips']: -- cgit From 50b7db2ab71c40732a979b1f424bd60627a74768 Mon Sep 17 00:00:00 2001 From: "vladimir.p" Date: Thu, 18 Aug 2011 13:24:56 -0700 Subject: first cut on types & extra-data (only DB work, no tests) --- nova/db/api.py | 60 +++++++ nova/db/sqlalchemy/api.py | 180 ++++++++++++++++++++- .../versions/037_add_volume_types_and_extradata.py | 103 ++++++++++++ nova/db/sqlalchemy/models.py | 30 ++++ 4 files changed, 372 insertions(+), 1 deletion(-) create mode 100644 nova/db/sqlalchemy/migrate_repo/versions/037_add_volume_types_and_extradata.py (limited to 'nova/db') diff --git a/nova/db/api.py b/nova/db/api.py index b9ea8757c..47e73226a 100644 --- a/nova/db/api.py +++ b/nova/db/api.py @@ -1424,3 +1424,63 @@ def instance_type_extra_specs_update_or_create(context, instance_type_id, key/value pairs specified in the extra specs dict argument""" IMPL.instance_type_extra_specs_update_or_create(context, instance_type_id, extra_specs) + + +################## + + +def volume_type_create(context, values): + """Create a new volume type.""" + return IMPL.volume_type_create(context, values) + + +def volume_type_get_all(context, inactive=False): + """Get all volume types.""" + return IMPL.volume_type_get_all(context, inactive) + + +def volume_type_get(context, id): + """Get volume type by id.""" + return IMPL.volume_type_get(context, id) + + +def volume_type_get_by_name(context, name): + """Get volume type by name.""" + return IMPL.volume_type_get_by_name(context, name) + + +def volume_type_destroy(context, name): + """Delete a volume type.""" + return IMPL.volume_type_destroy(context, name) + + +def volume_type_purge(context, name): + """Purges (removes) a volume type from DB. + + Use volume_type_destroy for most cases + + """ + return IMPL.volume_type_purge(context, name) + + +#################### + + +def volume_type_extra_specs_get(context, volume_type_id): + """Get all extra specs for a volume type.""" + return IMPL.volume_type_extra_specs_get(context, volume_type_id) + + +def volume_type_extra_specs_delete(context, volume_type_id, key): + """Delete the given extra specs item.""" + IMPL.volume_type_extra_specs_delete(context, volume_type_id, key) + + +def volume_type_extra_specs_update_or_create(context, volume_type_id, + extra_specs): + """Create or update volume type extra specs. This adds or modifies the + key/value pairs specified in the extra specs dict argument""" + IMPL.volume_type_extra_specs_update_or_create(context, volume_type_id, + extra_specs) + + diff --git a/nova/db/sqlalchemy/api.py b/nova/db/sqlalchemy/api.py index 95ec3f715..ce1066e42 100644 --- a/nova/db/sqlalchemy/api.py +++ b/nova/db/sqlalchemy/api.py @@ -3080,7 +3080,7 @@ def instance_type_create(_context, values): def _dict_with_extra_specs(inst_type_query): - """Takes an instance type query returned by sqlalchemy + """Takes an instance OR volume type query returned by sqlalchemy and returns it as a dictionary, converting the extra_specs entry from a list of dicts: @@ -3462,3 +3462,181 @@ def instance_type_extra_specs_update_or_create(context, instance_type_id, "deleted": 0}) spec_ref.save(session=session) return specs + + +################## + + +@require_admin_context +def volume_type_create(_context, values): + """Create a new instance type. In order to pass in extra specs, + the values dict should contain a 'extra_specs' key/value pair: + + {'extra_specs' : {'k1': 'v1', 'k2': 'v2', ...}} + + """ + try: + specs = values.get('extra_specs') + specs_refs = [] + if specs: + for k, v in specs.iteritems(): + specs_ref = models.VolumeTypeExtraSpecs() + specs_ref['key'] = k + specs_ref['value'] = v + specs_refs.append(specs_ref) + values['extra_specs'] = specs_refs + volume_type_ref = models.VolumeTypes() + volume_type_ref.update(values) + volume_type_ref.save() + except Exception, e: + raise exception.DBError(e) + return volume_type_ref + + +@require_context +def volume_type_get_all(context, inactive=False): + """ + Returns a dict describing all volume_types with name as key. + """ + session = get_session() + if inactive: + inst_types = session.query(models.VolumeTypes).\ + options(joinedload('extra_specs')).\ + order_by("name").\ + all() + else: + inst_types = session.query(models.VolumeTypes).\ + options(joinedload('extra_specs')).\ + filter_by(deleted=False).\ + order_by("name").\ + all() + inst_dict = {} + if inst_types: + for i in inst_types: + inst_dict[i['name']] = _dict_with_extra_specs(i) + return inst_dict + + +@require_context +def volume_type_get(context, id): + """Returns a dict describing specific volume_type""" + session = get_session() + inst_type = session.query(models.VolumeTypes).\ + options(joinedload('extra_specs')).\ + filter_by(id=id).\ + first() + + if not inst_type: + raise exception.VolumeTypeNotFound(volume_type=id) + else: + return _dict_with_extra_specs(inst_type) + + +@require_context +def volume_type_get_by_name(context, name): + """Returns a dict describing specific volume_type""" + session = get_session() + inst_type = session.query(models.VolumeTypes).\ + options(joinedload('extra_specs')).\ + filter_by(name=name).\ + first() + if not inst_type: + raise exception.VolumeTypeNotFoundByName(volume_type_name=name) + else: + return _dict_with_extra_specs(inst_type) + + +@require_admin_context +def volume_type_destroy(context, name): + """ Marks specific volume_type as deleted""" + session = get_session() + volume_type_ref = session.query(models.VolumeTypes).\ + filter_by(name=name) + records = volume_type_ref.update(dict(deleted=True)) + if records == 0: + raise exception.VolumeTypeNotFoundByName(volume_type_name=name) + else: + return volume_type_ref + + +@require_admin_context +def volume_type_purge(context, name): + """ Removes specific volume_type from DB + Usually volume_type_destroy should be used + """ + session = get_session() + volume_type_ref = session.query(models.VolumeTypes).\ + filter_by(name=name) + records = volume_type_ref.delete() + if records == 0: + raise exception.VolumeTypeNotFoundByName(volume_type_name=name) + else: + return volume_type_ref + + +#################### + + +@require_context +def volume_type_extra_specs_get(context, volume_type_id): + session = get_session() + + spec_results = session.query(models.VolumeTypeExtraSpecs).\ + filter_by(volume_type_id=volume_type_id).\ + filter_by(deleted=False).\ + all() + + spec_dict = {} + for i in spec_results: + spec_dict[i['key']] = i['value'] + return spec_dict + + +@require_context +def volume_type_extra_specs_delete(context, volume_type_id, key): + session = get_session() + session.query(models.VolumeTypeExtraSpecs).\ + filter_by(volume_type_id=volume_type_id).\ + filter_by(key=key).\ + filter_by(deleted=False).\ + update({'deleted': True, + 'deleted_at': utils.utcnow(), + 'updated_at': literal_column('updated_at')}) + + +@require_context +def volume_type_extra_specs_get_item(context, volume_type_id, key, + session=None): + + if not session: + session = get_session() + + spec_result = session.query(models.VolumeTypeExtraSpecs).\ + filter_by(volume_type_id=volume_type_id).\ + filter_by(key=key).\ + filter_by(deleted=False).\ + first() + + if not spec_result: + raise exception.\ + VolumeTypeExtraSpecsNotFound(extra_specs_key=key, + volume_type_id=volume_type_id) + return spec_result + + +@require_context +def volume_type_extra_specs_update_or_create(context, volume_type_id, + specs): + session = get_session() + spec_ref = None + for key, value in specs.iteritems(): + try: + spec_ref = volume_type_extra_specs_get_item( + context, volume_type_id, key, session) + except exception.VolumeTypeExtraSpecsNotFound, e: + spec_ref = models.VolumeTypeExtraSpecs() + spec_ref.update({"key": key, "value": value, + "volume_type_id": volume_type_id, + "deleted": 0}) + spec_ref.save(session=session) + return specs diff --git a/nova/db/sqlalchemy/migrate_repo/versions/037_add_volume_types_and_extradata.py b/nova/db/sqlalchemy/migrate_repo/versions/037_add_volume_types_and_extradata.py new file mode 100644 index 000000000..1bfa26845 --- /dev/null +++ b/nova/db/sqlalchemy/migrate_repo/versions/037_add_volume_types_and_extradata.py @@ -0,0 +1,103 @@ +# vim: tabstop=4 shiftwidth=4 softtabstop=4 + +# Copyright (c) 2011 Zadara Storage Inc. +# Copyright (c) 2011 OpenStack LLC. +# All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. + +from sqlalchemy import Column, DateTime, Integer, MetaData, String, Table +from sqlalchemy import Text, Boolean, ForeignKey + +from nova import log as logging + +meta = MetaData() + +# Just for the ForeignKey and column creation to succeed, these are not the +# actual definitions of tables . +# + +volumes = Table('volumes', meta, + Column('id', Integer(), primary_key=True, nullable=False), + ) + +volume_type_id = Column('volume_type_id', Integer(), nullable=True) + + +# New Tables +# + +volume_types = Table('volume_types', meta, + Column('created_at', DateTime(timezone=False)), + Column('updated_at', DateTime(timezone=False)), + Column('deleted_at', DateTime(timezone=False)), + Column('deleted', Boolean(create_constraint=True, name=None)), + Column('id', Integer(), primary_key=True, nullable=False), + Column('name', + String(length=255, convert_unicode=False, assert_unicode=None, + unicode_error=None, _warn_on_bytestring=False))) + +volume_type_extra_specs_table = Table('volume_type_extra_specs', meta, + Column('created_at', DateTime(timezone=False)), + Column('updated_at', DateTime(timezone=False)), + Column('deleted_at', DateTime(timezone=False)), + Column('deleted', Boolean(create_constraint=True, name=None)), + Column('id', Integer(), primary_key=True, nullable=False), + Column('volume_type_id', + Integer(), + ForeignKey('volume_types.id'), + nullable=False), + Column('key', + String(length=255, convert_unicode=False, assert_unicode=None, + unicode_error=None, _warn_on_bytestring=False)), + Column('value', + String(length=255, convert_unicode=False, assert_unicode=None, + unicode_error=None, _warn_on_bytestring=False))) + + +new_tables = (volume_types, volume_type_extra_specs_table) + +# +# Tables to alter +# + + +def upgrade(migrate_engine): + + from nova import context + from nova import db + from nova import flags + + FLAGS = flags.FLAGS + + # Upgrade operations go here. Don't create your own engine; + # bind migrate_engine to your metadata + meta.bind = migrate_engine + + for table in new_tables: + try: + table.create() + except Exception: + logging.info(repr(table)) + logging.exception('Exception while creating table') + raise + + volumes.create_column(volume_type_id) + +def downgrade(migrate_engine): + meta.bind = migrate_engine + + volumes.drop_column(volume_type_id) + + for table in new_tables: + table.drop() diff --git a/nova/db/sqlalchemy/models.py b/nova/db/sqlalchemy/models.py index f2a4680b0..70834ddb5 100644 --- a/nova/db/sqlalchemy/models.py +++ b/nova/db/sqlalchemy/models.py @@ -312,6 +312,36 @@ class Volume(BASE, NovaBase): provider_location = Column(String(255)) provider_auth = Column(String(255)) + volume_type_id = Column(Integer) + + +class VolumeTypes(BASE, NovaBase): + """Represent possible volume_types of volumes offered""" + __tablename__ = "volume_types" + id = Column(Integer, primary_key=True) + name = Column(String(255), unique=True) + + volumes = relationship(Volume, + backref=backref('volume_type', uselist=False), + foreign_keys=id, + primaryjoin='and_(Volume.volume_type_id == ' + 'VolumeTypes.id)') + + +class VolumeTypeExtraSpecs(BASE, NovaBase): + """Represents additional specs as key/value pairs for a volume_type""" + __tablename__ = 'volume_type_extra_specs' + id = Column(Integer, primary_key=True) + key = Column(String(255)) + value = Column(String(255)) + volume_type_id = Column(Integer, ForeignKey('volume_types.id'), + nullable=False) + volume_type = relationship(VolumeTypes, backref="extra_specs", + foreign_keys=volume_type_id, + primaryjoin='and_(' + 'VolumeTypeExtraSpecs.instance_type_id == VolumeTypes.id,' + 'VolumeTypeExtraSpecs.deleted == False)') + class Quota(BASE, NovaBase): """Represents a single quota override for a project. -- cgit From 7399805b96cefd9d0f88cec202edd9fdb2c91ec0 Mon Sep 17 00:00:00 2001 From: "vladimir.p" Date: Thu, 18 Aug 2011 13:38:11 -0700 Subject: typo --- nova/db/sqlalchemy/models.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'nova/db') diff --git a/nova/db/sqlalchemy/models.py b/nova/db/sqlalchemy/models.py index 70834ddb5..08ce34647 100644 --- a/nova/db/sqlalchemy/models.py +++ b/nova/db/sqlalchemy/models.py @@ -339,7 +339,7 @@ class VolumeTypeExtraSpecs(BASE, NovaBase): volume_type = relationship(VolumeTypes, backref="extra_specs", foreign_keys=volume_type_id, primaryjoin='and_(' - 'VolumeTypeExtraSpecs.instance_type_id == VolumeTypes.id,' + 'VolumeTypeExtraSpecs.volume_type_id == VolumeTypes.id,' 'VolumeTypeExtraSpecs.deleted == False)') -- cgit From b703b33cdd48c2409205504ef09cc91d287862bf Mon Sep 17 00:00:00 2001 From: "vladimir.p" Date: Thu, 18 Aug 2011 14:40:05 -0700 Subject: added unittests for volume_extra_data --- nova/db/sqlalchemy/api.py | 26 +++++++++++----------- .../versions/037_add_volume_types_and_extradata.py | 9 -------- 2 files changed, 13 insertions(+), 22 deletions(-) (limited to 'nova/db') diff --git a/nova/db/sqlalchemy/api.py b/nova/db/sqlalchemy/api.py index ce1066e42..a57133b72 100644 --- a/nova/db/sqlalchemy/api.py +++ b/nova/db/sqlalchemy/api.py @@ -3500,50 +3500,50 @@ def volume_type_get_all(context, inactive=False): """ session = get_session() if inactive: - inst_types = session.query(models.VolumeTypes).\ + vol_types = session.query(models.VolumeTypes).\ options(joinedload('extra_specs')).\ order_by("name").\ all() else: - inst_types = session.query(models.VolumeTypes).\ + vol_types = session.query(models.VolumeTypes).\ options(joinedload('extra_specs')).\ filter_by(deleted=False).\ order_by("name").\ all() - inst_dict = {} - if inst_types: - for i in inst_types: - inst_dict[i['name']] = _dict_with_extra_specs(i) - return inst_dict + vol_dict = {} + if vol_types: + for i in vol_types: + vol_dict[i['name']] = _dict_with_extra_specs(i) + return vol_dict @require_context def volume_type_get(context, id): """Returns a dict describing specific volume_type""" session = get_session() - inst_type = session.query(models.VolumeTypes).\ + vol_type = session.query(models.VolumeTypes).\ options(joinedload('extra_specs')).\ filter_by(id=id).\ first() - if not inst_type: + if not vol_type: raise exception.VolumeTypeNotFound(volume_type=id) else: - return _dict_with_extra_specs(inst_type) + return _dict_with_extra_specs(vol_type) @require_context def volume_type_get_by_name(context, name): """Returns a dict describing specific volume_type""" session = get_session() - inst_type = session.query(models.VolumeTypes).\ + vol_type = session.query(models.VolumeTypes).\ options(joinedload('extra_specs')).\ filter_by(name=name).\ first() - if not inst_type: + if not vol_type: raise exception.VolumeTypeNotFoundByName(volume_type_name=name) else: - return _dict_with_extra_specs(inst_type) + return _dict_with_extra_specs(vol_type) @require_admin_context diff --git a/nova/db/sqlalchemy/migrate_repo/versions/037_add_volume_types_and_extradata.py b/nova/db/sqlalchemy/migrate_repo/versions/037_add_volume_types_and_extradata.py index 1bfa26845..ed8eeb172 100644 --- a/nova/db/sqlalchemy/migrate_repo/versions/037_add_volume_types_and_extradata.py +++ b/nova/db/sqlalchemy/migrate_repo/versions/037_add_volume_types_and_extradata.py @@ -73,15 +73,6 @@ new_tables = (volume_types, volume_type_extra_specs_table) def upgrade(migrate_engine): - - from nova import context - from nova import db - from nova import flags - - FLAGS = flags.FLAGS - - # Upgrade operations go here. Don't create your own engine; - # bind migrate_engine to your metadata meta.bind = migrate_engine for table in new_tables: -- cgit From ef3f02fb37d49ccf6099e012bc27b87d7859a306 Mon Sep 17 00:00:00 2001 From: "vladimir.p" Date: Thu, 18 Aug 2011 15:42:30 -0700 Subject: added volume metadata. Fixed test_volume_types_extra_specs --- nova/db/api.py | 18 +++ nova/db/sqlalchemy/api.py | 140 +++++++++++++++++++++ .../versions/037_add_volume_types_and_extradata.py | 20 ++- nova/db/sqlalchemy/models.py | 15 +++ 4 files changed, 192 insertions(+), 1 deletion(-) (limited to 'nova/db') diff --git a/nova/db/api.py b/nova/db/api.py index 47e73226a..494d27708 100644 --- a/nova/db/api.py +++ b/nova/db/api.py @@ -1429,6 +1429,24 @@ def instance_type_extra_specs_update_or_create(context, instance_type_id, ################## +def volume_metadata_get(context, volume_id): + """Get all metadata for a volume.""" + return IMPL.volume_metadata_get(context, volume_id) + + +def volume_metadata_delete(context, volume_id, key): + """Delete the given metadata item.""" + IMPL.volume_metadata_delete(context, volume_id, key) + + +def volume_metadata_update(context, volume_id, metadata, delete): + """Update metadata if it exists, otherwise create it.""" + IMPL.volume_metadata_update(context, volume_id, metadata, delete) + + +################## + + def volume_type_create(context, values): """Create a new volume type.""" return IMPL.volume_type_create(context, values) diff --git a/nova/db/sqlalchemy/api.py b/nova/db/sqlalchemy/api.py index a57133b72..143162fc6 100644 --- a/nova/db/sqlalchemy/api.py +++ b/nova/db/sqlalchemy/api.py @@ -132,6 +132,20 @@ def require_instance_exists(f): return wrapper +def require_volume_exists(f): + """Decorator to require the specified volume to exist. + + Requres the wrapped function to use context and volume_id as + their first two arguments. + """ + + def wrapper(context, volume_id, *args, **kwargs): + db.api.volume_get(context, volume_id) + return f(context, volume_id, *args, **kwargs) + wrapper.__name__ = f.__name__ + return wrapper + + ################### @@ -2083,6 +2097,8 @@ def volume_attached(context, volume_id, instance_id, mountpoint): @require_context def volume_create(context, values): + values['metadata'] = _metadata_refs(values.get('metadata')) + volume_ref = models.Volume() volume_ref.update(values) @@ -2119,6 +2135,11 @@ def volume_destroy(context, volume_id): session.query(models.IscsiTarget).\ filter_by(volume_id=volume_id).\ update({'volume_id': None}) + session.query(models.VolumeMetadata).\ + filter_by(volume_id=volume_id).\ + update({'deleted': True, + 'deleted_at': utils.utcnow(), + 'updated_at': literal_column('updated_at')}) @require_admin_context @@ -2142,12 +2163,16 @@ def volume_get(context, volume_id, session=None): if is_admin_context(context): result = session.query(models.Volume).\ options(joinedload('instance')).\ + options(joinedload('metadata')).\ + options(joinedload('volume_type')).\ filter_by(id=volume_id).\ filter_by(deleted=can_read_deleted(context)).\ first() elif is_user_context(context): result = session.query(models.Volume).\ options(joinedload('instance')).\ + options(joinedload('metadata')).\ + options(joinedload('volume_type')).\ filter_by(project_id=context.project_id).\ filter_by(id=volume_id).\ filter_by(deleted=False).\ @@ -2163,6 +2188,8 @@ def volume_get_all(context): session = get_session() return session.query(models.Volume).\ options(joinedload('instance')).\ + options(joinedload('metadata')).\ + options(joinedload('volume_type')).\ filter_by(deleted=can_read_deleted(context)).\ all() @@ -2172,6 +2199,8 @@ def volume_get_all_by_host(context, host): session = get_session() return session.query(models.Volume).\ options(joinedload('instance')).\ + options(joinedload('metadata')).\ + options(joinedload('volume_type')).\ filter_by(host=host).\ filter_by(deleted=can_read_deleted(context)).\ all() @@ -2181,6 +2210,8 @@ def volume_get_all_by_host(context, host): def volume_get_all_by_instance(context, instance_id): session = get_session() result = session.query(models.Volume).\ + options(joinedload('metadata')).\ + options(joinedload('volume_type')).\ filter_by(instance_id=instance_id).\ filter_by(deleted=False).\ all() @@ -2196,6 +2227,8 @@ def volume_get_all_by_project(context, project_id): session = get_session() return session.query(models.Volume).\ options(joinedload('instance')).\ + options(joinedload('metadata')).\ + options(joinedload('volume_type')).\ filter_by(project_id=project_id).\ filter_by(deleted=can_read_deleted(context)).\ all() @@ -2208,6 +2241,8 @@ def volume_get_instance(context, volume_id): filter_by(id=volume_id).\ filter_by(deleted=can_read_deleted(context)).\ options(joinedload('instance')).\ + options(joinedload('metadata')).\ + options(joinedload('volume_type')).\ first() if not result: raise exception.VolumeNotFound(volume_id=volume_id) @@ -2242,12 +2277,117 @@ def volume_get_iscsi_target_num(context, volume_id): @require_context def volume_update(context, volume_id, values): session = get_session() + metadata = values.get('metadata') + if metadata is not None: + volume_metadata_update(context, + volume_id, + values.pop('metadata'), + delete=True) with session.begin(): volume_ref = volume_get(context, volume_id, session=session) volume_ref.update(values) volume_ref.save(session=session) + +#################### + + +@require_context +@require_volume_exists +def volume_metadata_get(context, volume_id): + session = get_session() + + meta_results = session.query(models.VolumeMetadata).\ + filter_by(volume_id=volume_id).\ + filter_by(deleted=False).\ + all() + + meta_dict = {} + for i in meta_results: + meta_dict[i['key']] = i['value'] + return meta_dict + + +@require_context +@require_volume_exists +def volume_metadata_delete(context, volume_id, key): + session = get_session() + session.query(models.VolumeMetadata).\ + filter_by(volume_id=volume_id).\ + filter_by(key=key).\ + filter_by(deleted=False).\ + update({'deleted': True, + 'deleted_at': utils.utcnow(), + 'updated_at': literal_column('updated_at')}) + + +@require_context +@require_volume_exists +def volume_metadata_delete_all(context, volume_id): + session = get_session() + session.query(models.VolumeMetadata).\ + filter_by(volume_id=volume_id).\ + filter_by(deleted=False).\ + update({'deleted': True, + 'deleted_at': utils.utcnow(), + 'updated_at': literal_column('updated_at')}) + + +@require_context +@require_volume_exists +def volume_metadata_get_item(context, volume_id, key, session=None): + if not session: + session = get_session() + + meta_result = session.query(models.VolumeMetadata).\ + filter_by(volume_id=volume_id).\ + filter_by(key=key).\ + filter_by(deleted=False).\ + first() + + if not meta_result: + raise exception.VolumeMetadataNotFound(metadata_key=key, + volume_id=volume_id) + return meta_result + + +@require_context +@require_volume_exists +def volume_metadata_update(context, volume_id, metadata, delete): + session = get_session() + + # Set existing metadata to deleted if delete argument is True + if delete: + original_metadata = volume_metadata_get(context, volume_id) + for meta_key, meta_value in original_metadata.iteritems(): + if meta_key not in metadata: + meta_ref = volume_metadata_get_item(context, volume_id, + meta_key, session) + meta_ref.update({'deleted': True}) + meta_ref.save(session=session) + + meta_ref = None + + # Now update all existing items with new values, or create new meta objects + for meta_key, meta_value in metadata.iteritems(): + + # update the value whether it exists or not + item = {"value": meta_value} + + try: + meta_ref = volume_metadata_get_item(context, volume_id, + meta_key, session) + except exception.VolumeMetadataNotFound, e: + meta_ref = models.VolumeMetadata() + item.update({"key": meta_key, "volume_id": volume_id}) + + meta_ref.update(item) + meta_ref.save(session=session) + + return metadata + + ################### diff --git a/nova/db/sqlalchemy/migrate_repo/versions/037_add_volume_types_and_extradata.py b/nova/db/sqlalchemy/migrate_repo/versions/037_add_volume_types_and_extradata.py index ed8eeb172..fc365d2b2 100644 --- a/nova/db/sqlalchemy/migrate_repo/versions/037_add_volume_types_and_extradata.py +++ b/nova/db/sqlalchemy/migrate_repo/versions/037_add_volume_types_and_extradata.py @@ -65,7 +65,25 @@ volume_type_extra_specs_table = Table('volume_type_extra_specs', meta, unicode_error=None, _warn_on_bytestring=False))) -new_tables = (volume_types, volume_type_extra_specs_table) +volume_metadata_table = Table('volume_metadata', meta, + Column('created_at', DateTime(timezone=False)), + Column('updated_at', DateTime(timezone=False)), + Column('deleted_at', DateTime(timezone=False)), + Column('deleted', Boolean(create_constraint=True, name=None)), + Column('id', Integer(), primary_key=True, nullable=False), + Column('volume_id', + Integer(), + ForeignKey('volumes.id'), + nullable=False), + Column('key', + String(length=255, convert_unicode=False, assert_unicode=None, + unicode_error=None, _warn_on_bytestring=False)), + Column('value', + String(length=255, convert_unicode=False, assert_unicode=None, + unicode_error=None, _warn_on_bytestring=False))) + + +new_tables = (volume_types, volume_type_extra_specs_table, volume_metadata_table) # # Tables to alter diff --git a/nova/db/sqlalchemy/models.py b/nova/db/sqlalchemy/models.py index 08ce34647..99e6f412e 100644 --- a/nova/db/sqlalchemy/models.py +++ b/nova/db/sqlalchemy/models.py @@ -315,6 +315,20 @@ class Volume(BASE, NovaBase): volume_type_id = Column(Integer) +class VolumeMetadata(BASE, NovaBase): + """Represents a metadata key/value pair for a volume""" + __tablename__ = 'volume_metadata' + id = Column(Integer, primary_key=True) + key = Column(String(255)) + value = Column(String(255)) + volume_id = Column(Integer, ForeignKey('volumes.id'), nullable=False) + volume = relationship(Volume, backref="metadata", + foreign_keys=volume_id, + primaryjoin='and_(' + 'VolumeMetadata.volume_id == Volume.id,' + 'VolumeMetadata.deleted == False)') + + class VolumeTypes(BASE, NovaBase): """Represent possible volume_types of volumes offered""" __tablename__ = "volume_types" @@ -824,6 +838,7 @@ def register_models(): Network, SecurityGroup, SecurityGroupIngressRule, SecurityGroupInstanceAssociation, AuthToken, User, Project, Certificate, ConsolePool, Console, Zone, + VolumeMetadata, VolumeTypes, VolumeTypeExtraSpecs, AgentBuild, InstanceMetadata, InstanceTypeExtraSpecs, Migration) engine = create_engine(FLAGS.sql_connection, echo=False) for model in models: -- cgit From 83d4c5b9b1f7ed9b75ae04464423b7ca4b5d627d Mon Sep 17 00:00:00 2001 From: Christopher MacGown Date: Fri, 19 Aug 2011 08:08:23 -0700 Subject: Fix config_drive migration, per Matt Dietz. --- .../versions/037_add_config_drive_to_instances.py | 11 +++-------- 1 file changed, 3 insertions(+), 8 deletions(-) (limited to 'nova/db') diff --git a/nova/db/sqlalchemy/migrate_repo/versions/037_add_config_drive_to_instances.py b/nova/db/sqlalchemy/migrate_repo/versions/037_add_config_drive_to_instances.py index 65ea012dd..36a6af16f 100644 --- a/nova/db/sqlalchemy/migrate_repo/versions/037_add_config_drive_to_instances.py +++ b/nova/db/sqlalchemy/migrate_repo/versions/037_add_config_drive_to_instances.py @@ -23,20 +23,15 @@ meta = MetaData() instances = Table("instances", meta, Column("id", Integer(), primary_key=True, nullable=False)) -config_drive_column = Column("config_drive", String(255)) # matches image_ref + +# matches the size of an image_ref +config_drive_column = Column("config_drive", String(255), nullable=True) def upgrade(migrate_engine): meta.bind = migrate_engine instances.create_column(config_drive_column) - rows = migrate_engine.execute(instances.select()) - for row in rows: - instance_config_drive = None # pre-existing instances don't have one. - migrate_engine.execute(instances.update()\ - .where(instances.c.id == row[0])\ - .values(config_drive=instance_config_drive)) - def downgrade(migrate_engine): meta.bind = migrate_engine -- cgit From c4fc9f0737ec9f8d5c950b850fed9930a68164f4 Mon Sep 17 00:00:00 2001 From: Christopher MacGown Date: Fri, 19 Aug 2011 08:44:14 -0700 Subject: Add copyright notices --- .../migrate_repo/versions/037_add_config_drive_to_instances.py | 4 ++-- nova/db/sqlalchemy/models.py | 1 + 2 files changed, 3 insertions(+), 2 deletions(-) (limited to 'nova/db') diff --git a/nova/db/sqlalchemy/migrate_repo/versions/037_add_config_drive_to_instances.py b/nova/db/sqlalchemy/migrate_repo/versions/037_add_config_drive_to_instances.py index 36a6af16f..d3058f00d 100644 --- a/nova/db/sqlalchemy/migrate_repo/versions/037_add_config_drive_to_instances.py +++ b/nova/db/sqlalchemy/migrate_repo/versions/037_add_config_drive_to_instances.py @@ -1,6 +1,6 @@ # vim: tabstop=4 shiftwidth=4 softtabstop=4 - -# Copyright 2011 OpenStack LLC. +# +# Copyright 2011 Piston Cloud Computing, Inc. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain diff --git a/nova/db/sqlalchemy/models.py b/nova/db/sqlalchemy/models.py index 8a6e2f673..c454cfcc3 100644 --- a/nova/db/sqlalchemy/models.py +++ b/nova/db/sqlalchemy/models.py @@ -2,6 +2,7 @@ # Copyright 2010 United States Government as represented by the # Administrator of the National Aeronautics and Space Administration. +# Copyright 2011 Piston Cloud Computing, Inc. # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may -- cgit From 1d121a42f5072026a3ad19cb5fd1915d7cd2ff63 Mon Sep 17 00:00:00 2001 From: "vladimir.p" Date: Mon, 22 Aug 2011 17:31:48 -0700 Subject: initial cut on volume type APIs --- nova/db/sqlalchemy/api.py | 16 ++++++++-------- nova/db/sqlalchemy/models.py | 2 +- 2 files changed, 9 insertions(+), 9 deletions(-) (limited to 'nova/db') diff --git a/nova/db/sqlalchemy/api.py b/nova/db/sqlalchemy/api.py index 143162fc6..ac8aed307 100644 --- a/nova/db/sqlalchemy/api.py +++ b/nova/db/sqlalchemy/api.py @@ -2163,7 +2163,7 @@ def volume_get(context, volume_id, session=None): if is_admin_context(context): result = session.query(models.Volume).\ options(joinedload('instance')).\ - options(joinedload('metadata')).\ + options(joinedload('volume_metadata')).\ options(joinedload('volume_type')).\ filter_by(id=volume_id).\ filter_by(deleted=can_read_deleted(context)).\ @@ -2171,7 +2171,7 @@ def volume_get(context, volume_id, session=None): elif is_user_context(context): result = session.query(models.Volume).\ options(joinedload('instance')).\ - options(joinedload('metadata')).\ + options(joinedload('volume_metadata')).\ options(joinedload('volume_type')).\ filter_by(project_id=context.project_id).\ filter_by(id=volume_id).\ @@ -2188,7 +2188,7 @@ def volume_get_all(context): session = get_session() return session.query(models.Volume).\ options(joinedload('instance')).\ - options(joinedload('metadata')).\ + options(joinedload('volume_metadata')).\ options(joinedload('volume_type')).\ filter_by(deleted=can_read_deleted(context)).\ all() @@ -2199,7 +2199,7 @@ def volume_get_all_by_host(context, host): session = get_session() return session.query(models.Volume).\ options(joinedload('instance')).\ - options(joinedload('metadata')).\ + options(joinedload('volume_metadata')).\ options(joinedload('volume_type')).\ filter_by(host=host).\ filter_by(deleted=can_read_deleted(context)).\ @@ -2210,7 +2210,7 @@ def volume_get_all_by_host(context, host): def volume_get_all_by_instance(context, instance_id): session = get_session() result = session.query(models.Volume).\ - options(joinedload('metadata')).\ + options(joinedload('volume_metadata')).\ options(joinedload('volume_type')).\ filter_by(instance_id=instance_id).\ filter_by(deleted=False).\ @@ -2227,7 +2227,7 @@ def volume_get_all_by_project(context, project_id): session = get_session() return session.query(models.Volume).\ options(joinedload('instance')).\ - options(joinedload('metadata')).\ + options(joinedload('volume_metadata')).\ options(joinedload('volume_type')).\ filter_by(project_id=project_id).\ filter_by(deleted=can_read_deleted(context)).\ @@ -2241,7 +2241,7 @@ def volume_get_instance(context, volume_id): filter_by(id=volume_id).\ filter_by(deleted=can_read_deleted(context)).\ options(joinedload('instance')).\ - options(joinedload('metadata')).\ + options(joinedload('volume_metadata')).\ options(joinedload('volume_type')).\ first() if not result: @@ -3634,7 +3634,7 @@ def volume_type_create(_context, values): @require_context -def volume_type_get_all(context, inactive=False): +def volume_type_get_all(context, inactive=False, filters={}): """ Returns a dict describing all volume_types with name as key. """ diff --git a/nova/db/sqlalchemy/models.py b/nova/db/sqlalchemy/models.py index 99e6f412e..4195ca113 100644 --- a/nova/db/sqlalchemy/models.py +++ b/nova/db/sqlalchemy/models.py @@ -322,7 +322,7 @@ class VolumeMetadata(BASE, NovaBase): key = Column(String(255)) value = Column(String(255)) volume_id = Column(Integer, ForeignKey('volumes.id'), nullable=False) - volume = relationship(Volume, backref="metadata", + volume = relationship(Volume, backref="volume_metadata", foreign_keys=volume_id, primaryjoin='and_(' 'VolumeMetadata.volume_id == Volume.id,' -- cgit From 7f1adb50cfab91a553f2d129b9b2eef1e5b2145b Mon Sep 17 00:00:00 2001 From: Christopher MacGown Date: Mon, 22 Aug 2011 22:17:51 -0700 Subject: Moved migration and fixed tests from upstream --- .../versions/041_add_config_drive_to_instances.py | 38 ++++++++++++++++++++++ 1 file changed, 38 insertions(+) create mode 100644 nova/db/sqlalchemy/migrate_repo/versions/041_add_config_drive_to_instances.py (limited to 'nova/db') diff --git a/nova/db/sqlalchemy/migrate_repo/versions/041_add_config_drive_to_instances.py b/nova/db/sqlalchemy/migrate_repo/versions/041_add_config_drive_to_instances.py new file mode 100644 index 000000000..d3058f00d --- /dev/null +++ b/nova/db/sqlalchemy/migrate_repo/versions/041_add_config_drive_to_instances.py @@ -0,0 +1,38 @@ +# vim: tabstop=4 shiftwidth=4 softtabstop=4 +# +# Copyright 2011 Piston Cloud Computing, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. + +from sqlalchemy import Column, Integer, MetaData, String, Table + +from nova import utils + + +meta = MetaData() + +instances = Table("instances", meta, + Column("id", Integer(), primary_key=True, nullable=False)) + +# matches the size of an image_ref +config_drive_column = Column("config_drive", String(255), nullable=True) + + +def upgrade(migrate_engine): + meta.bind = migrate_engine + instances.create_column(config_drive_column) + + +def downgrade(migrate_engine): + meta.bind = migrate_engine + instances.drop_column(config_drive_column) -- cgit From ddc7d9470674a4d7300d15e5c6fa54b784b6a36f Mon Sep 17 00:00:00 2001 From: "vladimir.p" Date: Tue, 23 Aug 2011 15:18:50 -0700 Subject: added volume_types APIs --- .../migrate_repo/versions/037_add_volume_types_and_extradata.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'nova/db') diff --git a/nova/db/sqlalchemy/migrate_repo/versions/037_add_volume_types_and_extradata.py b/nova/db/sqlalchemy/migrate_repo/versions/037_add_volume_types_and_extradata.py index fc365d2b2..27c8afcee 100644 --- a/nova/db/sqlalchemy/migrate_repo/versions/037_add_volume_types_and_extradata.py +++ b/nova/db/sqlalchemy/migrate_repo/versions/037_add_volume_types_and_extradata.py @@ -2,7 +2,6 @@ # Copyright (c) 2011 Zadara Storage Inc. # Copyright (c) 2011 OpenStack LLC. -# All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain @@ -45,7 +44,8 @@ volume_types = Table('volume_types', meta, Column('id', Integer(), primary_key=True, nullable=False), Column('name', String(length=255, convert_unicode=False, assert_unicode=None, - unicode_error=None, _warn_on_bytestring=False))) + unicode_error=None, _warn_on_bytestring=False), + unique=True)) volume_type_extra_specs_table = Table('volume_type_extra_specs', meta, Column('created_at', DateTime(timezone=False)), -- cgit From 29940dd27f3a40a4ad54bc2f7a4cea5ac2226b83 Mon Sep 17 00:00:00 2001 From: "vladimir.p" Date: Tue, 23 Aug 2011 20:22:27 -0700 Subject: added volume metadata APIs (OS & volume layers), search volume by metadata & other --- nova/db/sqlalchemy/api.py | 24 ++++++++++-------------- 1 file changed, 10 insertions(+), 14 deletions(-) (limited to 'nova/db') diff --git a/nova/db/sqlalchemy/api.py b/nova/db/sqlalchemy/api.py index ac8aed307..f14f95ab0 100644 --- a/nova/db/sqlalchemy/api.py +++ b/nova/db/sqlalchemy/api.py @@ -1020,11 +1020,11 @@ def virtual_interface_delete_by_instance(context, instance_id): ################### -def _metadata_refs(metadata_dict): +def _metadata_refs(metadata_dict, meta_class): metadata_refs = [] if metadata_dict: for k, v in metadata_dict.iteritems(): - metadata_ref = models.InstanceMetadata() + metadata_ref = meta_class() metadata_ref['key'] = k metadata_ref['value'] = v metadata_refs.append(metadata_ref) @@ -1038,8 +1038,8 @@ def instance_create(context, values): context - request context object values - dict containing column values. """ - values['metadata'] = _metadata_refs(values.get('metadata')) - + values['metadata'] = _metadata_refs(values.get('metadata'), + models.InstanceMetadata) instance_ref = models.Instance() instance_ref['uuid'] = str(utils.gen_uuid()) @@ -2097,8 +2097,8 @@ def volume_attached(context, volume_id, instance_id, mountpoint): @require_context def volume_create(context, values): - values['metadata'] = _metadata_refs(values.get('metadata')) - + values['volume_metadata'] = _metadata_refs(values.get('metadata'), + models.VolumeMetadata) volume_ref = models.Volume() volume_ref.update(values) @@ -3617,14 +3617,10 @@ def volume_type_create(_context, values): """ try: specs = values.get('extra_specs') - specs_refs = [] - if specs: - for k, v in specs.iteritems(): - specs_ref = models.VolumeTypeExtraSpecs() - specs_ref['key'] = k - specs_ref['value'] = v - specs_refs.append(specs_ref) - values['extra_specs'] = specs_refs + + values['extra_specs'] = _metadata_refs(values.get('extra_specs'), + models.VolumeTypeExtraSpecs) + volume_type_ref = models.VolumeTypes() volume_type_ref.update(values) volume_type_ref.save() -- cgit From 0873a3c7b9a1a75c6e04bd1b66f8fbe4935585b2 Mon Sep 17 00:00:00 2001 From: Mark McLoughlin Date: Wed, 24 Aug 2011 15:49:53 +0100 Subject: Fix flavorid migration failure With sqlalchemy 0.7.2 and migrate 0.7.1, I was seeing: Traceback (most recent call last): File "/usr/lib/python2.7/site-packages/nova/db/migration.py", line 37, in db_sync ret = IMPL.db_sync(version=version) [...] File "/usr/lib/python2.7/site-packages/nova/db/sqlalchemy/migrate_repo/versions/036_change_flavor_id_in_migrations.py", line 46, in upgrade .values(old_instance_type_id=instance_type.id)) [...] File "/usr/lib64/python2.7/site-packages/sqlalchemy/engine/default.py", line 301, in do_commit connection.commit() OperationalError: (OperationalError) database is locked None None It looks like the database is being held open as we iterate over the rows in the instance_types table. --- .../versions/036_change_flavor_id_in_migrations.py | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) (limited to 'nova/db') diff --git a/nova/db/sqlalchemy/migrate_repo/versions/036_change_flavor_id_in_migrations.py b/nova/db/sqlalchemy/migrate_repo/versions/036_change_flavor_id_in_migrations.py index f3244033b..dfbd4ba32 100644 --- a/nova/db/sqlalchemy/migrate_repo/versions/036_change_flavor_id_in_migrations.py +++ b/nova/db/sqlalchemy/migrate_repo/versions/036_change_flavor_id_in_migrations.py @@ -40,13 +40,17 @@ def upgrade(migrate_engine): migrations.create_column(new_instance_type_id) # Convert flavor_id to instance_type_id + itypes = {} for instance_type in migrate_engine.execute(instance_types.select()): + itypes[instance_type.id] = instance_type.flavorid + + for instance_type_id in itypes.keys(): migrate_engine.execute(migrations.update()\ - .where(migrations.c.old_flavor_id == instance_type.flavorid)\ - .values(old_instance_type_id=instance_type.id)) + .where(migrations.c.old_flavor_id == itypes[instance_type_id])\ + .values(old_instance_type_id=instance_type_id)) migrate_engine.execute(migrations.update()\ - .where(migrations.c.new_flavor_id == instance_type.flavorid)\ - .values(new_instance_type_id=instance_type.id)) + .where(migrations.c.new_flavor_id == itypes[instance_type_id])\ + .values(new_instance_type_id=instance_type_id)) migrations.c.old_flavor_id.drop() migrations.c.new_flavor_id.drop() -- cgit From b428ac4c20e44f537b0dedeefcc2637efbc998ea Mon Sep 17 00:00:00 2001 From: Mark McLoughlin Date: Wed, 24 Aug 2011 15:50:46 +0100 Subject: Fix quotas migration failure With sqlalchemy 0.7.2 and migrate 0.7.1, I was seeing: Traceback (most recent call last): File "/usr/lib/python2.7/site-packages/nova/db/migration.py", line 37, in db_sync ret = IMPL.db_sync(version=version) [..] File "/usr/lib/python2.7/site-packages/nova/db/sqlalchemy/migrate_repo/versions/016_make_quotas_key_and_value.py", line 189, in upgrade new_quotas.rename('quotas') [..] File "/usr/lib/python2.7/site-packages/migrate/changeset/schema.py", line 479, in deregister del meta.tables[key] File "/usr/lib64/python2.7/site-packages/sqlalchemy/util/_collections.py", line 38, in _immutable raise TypeError("%s object is immutable" % self.__class__.__name__) TypeError: immutabledict object is immutable This is actually a bug in sqlalchemy-migrate: http://code.google.com/p/sqlalchemy-migrate/issues/detail?id=128 But it can be worked around by ensuring there isn't a 'quotas' table in the metadata's table hash before renaming. --- .../versions/016_make_quotas_key_and_value.py | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) (limited to 'nova/db') diff --git a/nova/db/sqlalchemy/migrate_repo/versions/016_make_quotas_key_and_value.py b/nova/db/sqlalchemy/migrate_repo/versions/016_make_quotas_key_and_value.py index a4fe3e482..56b287171 100644 --- a/nova/db/sqlalchemy/migrate_repo/versions/016_make_quotas_key_and_value.py +++ b/nova/db/sqlalchemy/migrate_repo/versions/016_make_quotas_key_and_value.py @@ -75,8 +75,8 @@ def new_style_quotas_table(name): ) -def existing_quotas_table(migrate_engine): - return Table('quotas', meta, autoload=True, autoload_with=migrate_engine) +def quotas_table(migrate_engine, name='quotas'): + return Table(name, meta, autoload=True, autoload_with=migrate_engine) def _assert_no_duplicate_project_ids(quotas): @@ -179,13 +179,18 @@ def upgrade(migrate_engine): # bind migrate_engine to your metadata meta.bind = migrate_engine - old_quotas = existing_quotas_table(migrate_engine) + old_quotas = quotas_table(migrate_engine) assert_old_quotas_have_no_active_duplicates(migrate_engine, old_quotas) new_quotas = new_style_quotas_table('quotas_new') new_quotas.create() convert_forward(migrate_engine, old_quotas, new_quotas) old_quotas.drop() + + # clear metadata to work around this: + # http://code.google.com/p/sqlalchemy-migrate/issues/detail?id=128 + meta.clear() + new_quotas = quotas_table(migrate_engine, 'quotas_new') new_quotas.rename('quotas') @@ -193,11 +198,16 @@ def downgrade(migrate_engine): # Operations to reverse the above upgrade go here. meta.bind = migrate_engine - new_quotas = existing_quotas_table(migrate_engine) + new_quotas = quotas_table(migrate_engine) assert_new_quotas_have_no_active_duplicates(migrate_engine, new_quotas) old_quotas = old_style_quotas_table('quotas_old') old_quotas.create() convert_backward(migrate_engine, old_quotas, new_quotas) new_quotas.drop() + + # clear metadata to work around this: + # http://code.google.com/p/sqlalchemy-migrate/issues/detail?id=128 + meta.clear() + old_quotas = quotas_table(migrate_engine, 'quotas_old') old_quotas.rename('quotas') -- cgit From 8ad9373648dea11783545ad88429def4691a2925 Mon Sep 17 00:00:00 2001 From: "vladimir.p" Date: Wed, 24 Aug 2011 09:18:53 -0700 Subject: pep8 compliant --- nova/db/api.py | 2 -- nova/db/sqlalchemy/api.py | 4 +--- .../migrate_repo/versions/042_add_volume_types_and_extradata.py | 5 ++++- 3 files changed, 5 insertions(+), 6 deletions(-) (limited to 'nova/db') diff --git a/nova/db/api.py b/nova/db/api.py index 8726df6dd..3bb9b4970 100644 --- a/nova/db/api.py +++ b/nova/db/api.py @@ -1512,5 +1512,3 @@ def volume_type_extra_specs_update_or_create(context, volume_type_id, key/value pairs specified in the extra specs dict argument""" IMPL.volume_type_extra_specs_update_or_create(context, volume_type_id, extra_specs) - - diff --git a/nova/db/sqlalchemy/api.py b/nova/db/sqlalchemy/api.py index 5abdd71f9..4786f539f 100644 --- a/nova/db/sqlalchemy/api.py +++ b/nova/db/sqlalchemy/api.py @@ -2350,7 +2350,6 @@ def volume_update(context, volume_id, values): volume_ref.save(session=session) - #################### @@ -3682,8 +3681,7 @@ def volume_type_create(_context, values): specs = values.get('extra_specs') values['extra_specs'] = _metadata_refs(values.get('extra_specs'), - models.VolumeTypeExtraSpecs) - + models.VolumeTypeExtraSpecs) volume_type_ref = models.VolumeTypes() volume_type_ref.update(values) volume_type_ref.save() diff --git a/nova/db/sqlalchemy/migrate_repo/versions/042_add_volume_types_and_extradata.py b/nova/db/sqlalchemy/migrate_repo/versions/042_add_volume_types_and_extradata.py index 27c8afcee..dd4cccb9e 100644 --- a/nova/db/sqlalchemy/migrate_repo/versions/042_add_volume_types_and_extradata.py +++ b/nova/db/sqlalchemy/migrate_repo/versions/042_add_volume_types_and_extradata.py @@ -83,7 +83,9 @@ volume_metadata_table = Table('volume_metadata', meta, unicode_error=None, _warn_on_bytestring=False))) -new_tables = (volume_types, volume_type_extra_specs_table, volume_metadata_table) +new_tables = (volume_types, + volume_type_extra_specs_table, + volume_metadata_table) # # Tables to alter @@ -103,6 +105,7 @@ def upgrade(migrate_engine): volumes.create_column(volume_type_id) + def downgrade(migrate_engine): meta.bind = migrate_engine -- cgit From 576dd4a0dce66c7949a1f66a6979d9e1d11916bf Mon Sep 17 00:00:00 2001 From: "vladimir.p" Date: Wed, 24 Aug 2011 14:07:17 -0700 Subject: added Openstack APIs for volume types & extradata --- nova/db/sqlalchemy/api.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'nova/db') diff --git a/nova/db/sqlalchemy/api.py b/nova/db/sqlalchemy/api.py index 4786f539f..d1fbf8cab 100644 --- a/nova/db/sqlalchemy/api.py +++ b/nova/db/sqlalchemy/api.py @@ -3823,7 +3823,7 @@ def volume_type_extra_specs_get_item(context, volume_type_id, key, @require_context def volume_type_extra_specs_update_or_create(context, volume_type_id, - specs): + specs): session = get_session() spec_ref = None for key, value in specs.iteritems(): -- cgit From 48cd9689de31e408c792052747f714a9dbe1f8f7 Mon Sep 17 00:00:00 2001 From: "vladimir.p" Date: Wed, 24 Aug 2011 15:51:29 -0700 Subject: added virtio flag; associate address for VSA; cosmetic changes. Prior to volume_types merge --- nova/db/sqlalchemy/migrate_repo/versions/037_add_vsa_data.py | 1 - nova/db/sqlalchemy/session.py | 2 -- 2 files changed, 3 deletions(-) (limited to 'nova/db') diff --git a/nova/db/sqlalchemy/migrate_repo/versions/037_add_vsa_data.py b/nova/db/sqlalchemy/migrate_repo/versions/037_add_vsa_data.py index 5a80f4e7a..8a57bd234 100644 --- a/nova/db/sqlalchemy/migrate_repo/versions/037_add_vsa_data.py +++ b/nova/db/sqlalchemy/migrate_repo/versions/037_add_vsa_data.py @@ -2,7 +2,6 @@ # Copyright (c) 2011 Zadara Storage Inc. # Copyright (c) 2011 OpenStack LLC. -# All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain diff --git a/nova/db/sqlalchemy/session.py b/nova/db/sqlalchemy/session.py index 07f281938..c678cb543 100644 --- a/nova/db/sqlalchemy/session.py +++ b/nova/db/sqlalchemy/session.py @@ -30,11 +30,9 @@ import nova.exception import nova.flags import nova.log - FLAGS = nova.flags.FLAGS LOG = nova.log.getLogger("nova.db.sqlalchemy") - try: import MySQLdb except ImportError: -- cgit From 0c88dbc01ae9c10a3a83072ecdef201103a46752 Mon Sep 17 00:00:00 2001 From: "vladimir.p" Date: Wed, 24 Aug 2011 16:41:14 -0700 Subject: added new tables to list of DBs in migration.py --- nova/db/sqlalchemy/migration.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'nova/db') diff --git a/nova/db/sqlalchemy/migration.py b/nova/db/sqlalchemy/migration.py index d9e303599..765deb479 100644 --- a/nova/db/sqlalchemy/migration.py +++ b/nova/db/sqlalchemy/migration.py @@ -64,7 +64,8 @@ def db_version(): 'users', 'user_project_association', 'user_project_role_association', 'user_role_association', - 'volumes'): + 'volumes', 'volume_metadata', + 'volume_types', 'volume_type_extra_specs'): assert table in meta.tables return db_version_control(1) except AssertionError: -- cgit From 4834b920e3186712ab56e65a88c2e8c838d16f9c Mon Sep 17 00:00:00 2001 From: "vladimir.p" Date: Thu, 25 Aug 2011 18:38:35 -0700 Subject: VSA code redesign. Drive types completely replaced by Volume types --- nova/db/api.py | 45 ------ nova/db/sqlalchemy/api.py | 153 +-------------------- .../migrate_repo/versions/042_add_vsa_data.py | 133 ------------------ .../migrate_repo/versions/043_add_vsa_data.py | 75 ++++++++++ nova/db/sqlalchemy/models.py | 43 +----- nova/db/sqlalchemy/session.py | 2 + 6 files changed, 81 insertions(+), 370 deletions(-) delete mode 100644 nova/db/sqlalchemy/migrate_repo/versions/042_add_vsa_data.py create mode 100644 nova/db/sqlalchemy/migrate_repo/versions/043_add_vsa_data.py (limited to 'nova/db') diff --git a/nova/db/api.py b/nova/db/api.py index 354a90571..a2e581fe9 100644 --- a/nova/db/api.py +++ b/nova/db/api.py @@ -918,16 +918,6 @@ def volume_get_all_by_project(context, project_id): return IMPL.volume_get_all_by_project(context, project_id) -def volume_get_all_assigned_to_vsa(context, vsa_id): - """Get all volumes assigned to particular VSA.""" - return IMPL.volume_get_all_assigned_to_vsa(context, vsa_id) - - -def volume_get_all_assigned_from_vsa(context, vsa_id): - """Get all volumes created from particular VSA.""" - return IMPL.volume_get_all_assigned_from_vsa(context, vsa_id) - - def volume_get_by_ec2_id(context, ec2_id): """Get a volume by ec2 id.""" return IMPL.volume_get_by_ec2_id(context, ec2_id) @@ -1528,36 +1518,6 @@ def volume_type_extra_specs_update_or_create(context, volume_type_id, #################### -def drive_type_create(context, values): - """Creates drive type record.""" - return IMPL.drive_type_create(context, values) - - -def drive_type_update(context, drive_type_id, values): - """Updates drive type record.""" - return IMPL.drive_type_update(context, drive_type_id, values) - - -def drive_type_destroy(context, drive_type_id): - """Deletes drive type record.""" - return IMPL.drive_type_destroy(context, drive_type_id) - - -def drive_type_get(context, drive_type_id): - """Get drive type record by id.""" - return IMPL.drive_type_get(context, drive_type_id) - - -def drive_type_get_by_name(context, name): - """Get drive type record by name.""" - return IMPL.drive_type_get_by_name(context, name) - - -def drive_type_get_all(context, visible): - """Returns all (or only visible) drive types.""" - return IMPL.drive_type_get_all(context, visible) - - def vsa_create(context, values): """Creates Virtual Storage Array record.""" return IMPL.vsa_create(context, values) @@ -1586,8 +1546,3 @@ def vsa_get_all(context): def vsa_get_all_by_project(context, project_id): """Get all Virtual Storage Array records by project ID.""" return IMPL.vsa_get_all_by_project(context, project_id) - - -def vsa_get_vc_ips_list(context, vsa_id): - """Retrieves IPs of instances associated with Virtual Storage Array.""" - return IMPL.vsa_get_vc_ips_list(context, vsa_id) diff --git a/nova/db/sqlalchemy/api.py b/nova/db/sqlalchemy/api.py index 7a572f55a..65b09a65d 100644 --- a/nova/db/sqlalchemy/api.py +++ b/nova/db/sqlalchemy/api.py @@ -2226,7 +2226,6 @@ def volume_get(context, volume_id, session=None): options(joinedload('instance')).\ options(joinedload('volume_metadata')).\ options(joinedload('volume_type')).\ - options(joinedload('drive_type')).\ filter_by(id=volume_id).\ filter_by(deleted=can_read_deleted(context)).\ first() @@ -2235,7 +2234,6 @@ def volume_get(context, volume_id, session=None): options(joinedload('instance')).\ options(joinedload('volume_metadata')).\ options(joinedload('volume_type')).\ - options(joinedload('drive_type')).\ filter_by(project_id=context.project_id).\ filter_by(id=volume_id).\ filter_by(deleted=False).\ @@ -2253,7 +2251,6 @@ def volume_get_all(context): options(joinedload('instance')).\ options(joinedload('volume_metadata')).\ options(joinedload('volume_type')).\ - options(joinedload('drive_type')).\ filter_by(deleted=can_read_deleted(context)).\ all() @@ -2265,7 +2262,6 @@ def volume_get_all_by_host(context, host): options(joinedload('instance')).\ options(joinedload('volume_metadata')).\ options(joinedload('volume_type')).\ - options(joinedload('drive_type')).\ filter_by(host=host).\ filter_by(deleted=can_read_deleted(context)).\ all() @@ -2277,7 +2273,6 @@ def volume_get_all_by_instance(context, instance_id): result = session.query(models.Volume).\ options(joinedload('volume_metadata')).\ options(joinedload('volume_type')).\ - options(joinedload('drive_type')).\ filter_by(instance_id=instance_id).\ filter_by(deleted=False).\ all() @@ -2286,28 +2281,6 @@ def volume_get_all_by_instance(context, instance_id): return result -@require_admin_context -def volume_get_all_assigned_to_vsa(context, vsa_id): - session = get_session() - result = session.query(models.Volume).\ - options(joinedload('drive_type')).\ - filter_by(to_vsa_id=vsa_id).\ - filter_by(deleted=False).\ - all() - return result - - -@require_admin_context -def volume_get_all_assigned_from_vsa(context, vsa_id): - session = get_session() - result = session.query(models.Volume).\ - options(joinedload('drive_type')).\ - filter_by(from_vsa_id=vsa_id).\ - filter_by(deleted=False).\ - all() - return result - - @require_context def volume_get_all_by_project(context, project_id): authorize_project_context(context, project_id) @@ -2317,7 +2290,6 @@ def volume_get_all_by_project(context, project_id): options(joinedload('instance')).\ options(joinedload('volume_metadata')).\ options(joinedload('volume_type')).\ - options(joinedload('drive_type')).\ filter_by(project_id=project_id).\ filter_by(deleted=can_read_deleted(context)).\ all() @@ -2332,7 +2304,6 @@ def volume_get_instance(context, volume_id): options(joinedload('instance')).\ options(joinedload('volume_metadata')).\ options(joinedload('volume_type')).\ - options(joinedload('drive_type')).\ first() if not result: raise exception.VolumeNotFound(volume_id=volume_id) @@ -2377,7 +2348,7 @@ def volume_update(context, volume_id, values): volume_ref = volume_get(context, volume_id, session=session) volume_ref.update(values) volume_ref.save(session=session) - return volume_ref + #################### @@ -3871,106 +3842,6 @@ def volume_type_extra_specs_update_or_create(context, volume_type_id, #################### -@require_admin_context -def drive_type_create(context, values): - """ - Creates drive type record. - """ - try: - drive_type_ref = models.DriveTypes() - drive_type_ref.update(values) - drive_type_ref.save() - except Exception, e: - raise exception.DBError(e) - return drive_type_ref - - -@require_admin_context -def drive_type_update(context, drive_type_id, values): - """ - Updates drive type record. - """ - session = get_session() - with session.begin(): - drive_type_ref = drive_type_get(context, drive_type_id, - session=session) - drive_type_ref.update(values) - drive_type_ref.save(session=session) - return drive_type_ref - - -@require_admin_context -def drive_type_destroy(context, drive_type_id): - """ - Deletes drive type record. - """ - session = get_session() - drive_type_ref = session.query(models.DriveTypes).\ - filter_by(id=drive_type_id) - records = drive_type_ref.delete() - if records == 0: - raise exception.VirtualDiskTypeNotFound(id=drive_type_id) - - -@require_context -def drive_type_get(context, drive_type_id, session=None): - """ - Get drive type record by id. - """ - if not session: - session = get_session() - - result = session.query(models.DriveTypes).\ - filter_by(id=drive_type_id).\ - filter_by(deleted=can_read_deleted(context)).\ - first() - if not result: - raise exception.VirtualDiskTypeNotFound(id=drive_type_id) - - return result - - -@require_context -def drive_type_get_by_name(context, name, session=None): - """ - Get drive type record by name. - """ - if not session: - session = get_session() - - result = session.query(models.DriveTypes).\ - filter_by(name=name).\ - filter_by(deleted=can_read_deleted(context)).\ - first() - if not result: - raise exception.VirtualDiskTypeNotFoundByName(name=name) - - return result - - -@require_context -def drive_type_get_all(context, visible): - """ - Returns all (or only visible) drive types. - """ - session = get_session() - if visible: - drive_types = session.query(models.DriveTypes).\ - filter_by(deleted=can_read_deleted(context)).\ - filter_by(visible=True).\ - order_by("name").\ - all() - else: - drive_types = session.query(models.DriveTypes).\ - filter_by(deleted=can_read_deleted(context)).\ - order_by("name").\ - all() - return drive_types - - - #################### - - @require_admin_context def vsa_create(context, values): """ @@ -4067,26 +3938,4 @@ def vsa_get_all_by_project(context, project_id): all() -@require_context -def vsa_get_vc_ips_list(context, vsa_id): - """ - Retrieves IPs of instances associated with Virtual Storage Array. - """ - result = [] - - vc_instances = instance_get_all_by_filters(context, - search_opts={'metadata': dict(vsa_id=str(vsa_id))}) - for vc_instance in vc_instances: - if vc_instance['fixed_ips']: - for fixed in vc_instance['fixed_ips']: - # insert the [floating,fixed] (if exists) in the head, - # otherwise append the [none,fixed] in the tail - ip = {} - ip['fixed'] = fixed['address'] - if fixed['floating_ips']: - ip['floating'] = fixed['floating_ips'][0]['address'] - result.append(ip) - - return result - #################### diff --git a/nova/db/sqlalchemy/migrate_repo/versions/042_add_vsa_data.py b/nova/db/sqlalchemy/migrate_repo/versions/042_add_vsa_data.py deleted file mode 100644 index 8a57bd234..000000000 --- a/nova/db/sqlalchemy/migrate_repo/versions/042_add_vsa_data.py +++ /dev/null @@ -1,133 +0,0 @@ -# vim: tabstop=4 shiftwidth=4 softtabstop=4 - -# Copyright (c) 2011 Zadara Storage Inc. -# Copyright (c) 2011 OpenStack LLC. -# -# Licensed under the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. You may obtain -# a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations -# under the License. - -from sqlalchemy import Column, DateTime, Integer, MetaData, String, Table -from sqlalchemy import Text, Boolean, ForeignKey - -from nova import log as logging - -meta = MetaData() - -# Just for the ForeignKey and column creation to succeed, these are not the -# actual definitions of tables . -# - -volumes = Table('volumes', meta, - Column('id', Integer(), primary_key=True, nullable=False), - ) - -to_vsa_id = Column('to_vsa_id', Integer(), nullable=True) -from_vsa_id = Column('from_vsa_id', Integer(), nullable=True) -drive_type_id = Column('drive_type_id', Integer(), nullable=True) - - -# New Tables -# - -virtual_storage_arrays = Table('virtual_storage_arrays', meta, - Column('created_at', DateTime(timezone=False)), - Column('updated_at', DateTime(timezone=False)), - Column('deleted_at', DateTime(timezone=False)), - Column('deleted', Boolean(create_constraint=True, name=None)), - Column('id', Integer(), primary_key=True, nullable=False), - Column('display_name', - String(length=255, convert_unicode=False, assert_unicode=None, - unicode_error=None, _warn_on_bytestring=False)), - Column('display_description', - String(length=255, convert_unicode=False, assert_unicode=None, - unicode_error=None, _warn_on_bytestring=False)), - Column('project_id', - String(length=255, convert_unicode=False, assert_unicode=None, - unicode_error=None, _warn_on_bytestring=False)), - Column('availability_zone', - String(length=255, convert_unicode=False, assert_unicode=None, - unicode_error=None, _warn_on_bytestring=False)), - Column('instance_type_id', Integer(), nullable=False), - Column('image_ref', - String(length=255, convert_unicode=False, assert_unicode=None, - unicode_error=None, _warn_on_bytestring=False)), - Column('vc_count', Integer(), nullable=False), - Column('vol_count', Integer(), nullable=False), - Column('status', - String(length=255, convert_unicode=False, assert_unicode=None, - unicode_error=None, _warn_on_bytestring=False)), - ) - -drive_types = Table('drive_types', meta, - Column('created_at', DateTime(timezone=False)), - Column('updated_at', DateTime(timezone=False)), - Column('deleted_at', DateTime(timezone=False)), - Column('deleted', Boolean(create_constraint=True, name=None)), - Column('id', Integer(), primary_key=True, nullable=False), - Column('name', - String(length=255, convert_unicode=False, assert_unicode=None, - unicode_error=None, _warn_on_bytestring=False), - unique=True), - Column('type', - String(length=255, convert_unicode=False, assert_unicode=None, - unicode_error=None, _warn_on_bytestring=False)), - Column('size_gb', Integer(), nullable=False), - Column('rpm', - String(length=255, convert_unicode=False, assert_unicode=None, - unicode_error=None, _warn_on_bytestring=False)), - Column('capabilities', - String(length=255, convert_unicode=False, assert_unicode=None, - unicode_error=None, _warn_on_bytestring=False)), - Column('visible', Boolean(create_constraint=True, name=None)), - ) - -new_tables = (virtual_storage_arrays, drive_types) - -# -# Tables to alter -# - - -def upgrade(migrate_engine): - - from nova import context - from nova import db - from nova import flags - - FLAGS = flags.FLAGS - - # Upgrade operations go here. Don't create your own engine; - # bind migrate_engine to your metadata - meta.bind = migrate_engine - - for table in new_tables: - try: - table.create() - except Exception: - logging.info(repr(table)) - logging.exception('Exception while creating table') - raise - - volumes.create_column(to_vsa_id) - volumes.create_column(from_vsa_id) - volumes.create_column(drive_type_id) - - -def downgrade(migrate_engine): - meta.bind = migrate_engine - - volumes.drop_column(to_vsa_id) - volumes.drop_column(from_vsa_id) - volumes.drop_column(drive_type_id) - - for table in new_tables: - table.drop() diff --git a/nova/db/sqlalchemy/migrate_repo/versions/043_add_vsa_data.py b/nova/db/sqlalchemy/migrate_repo/versions/043_add_vsa_data.py new file mode 100644 index 000000000..844643704 --- /dev/null +++ b/nova/db/sqlalchemy/migrate_repo/versions/043_add_vsa_data.py @@ -0,0 +1,75 @@ +# vim: tabstop=4 shiftwidth=4 softtabstop=4 + +# Copyright (c) 2011 Zadara Storage Inc. +# Copyright (c) 2011 OpenStack LLC. +# +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. + +from sqlalchemy import Column, DateTime, Integer, MetaData, String, Table +from sqlalchemy import Text, Boolean, ForeignKey + +from nova import log as logging + +meta = MetaData() + +# +# New Tables +# + +virtual_storage_arrays = Table('virtual_storage_arrays', meta, + Column('created_at', DateTime(timezone=False)), + Column('updated_at', DateTime(timezone=False)), + Column('deleted_at', DateTime(timezone=False)), + Column('deleted', Boolean(create_constraint=True, name=None)), + Column('id', Integer(), primary_key=True, nullable=False), + Column('display_name', + String(length=255, convert_unicode=False, assert_unicode=None, + unicode_error=None, _warn_on_bytestring=False)), + Column('display_description', + String(length=255, convert_unicode=False, assert_unicode=None, + unicode_error=None, _warn_on_bytestring=False)), + Column('project_id', + String(length=255, convert_unicode=False, assert_unicode=None, + unicode_error=None, _warn_on_bytestring=False)), + Column('availability_zone', + String(length=255, convert_unicode=False, assert_unicode=None, + unicode_error=None, _warn_on_bytestring=False)), + Column('instance_type_id', Integer(), nullable=False), + Column('image_ref', + String(length=255, convert_unicode=False, assert_unicode=None, + unicode_error=None, _warn_on_bytestring=False)), + Column('vc_count', Integer(), nullable=False), + Column('vol_count', Integer(), nullable=False), + Column('status', + String(length=255, convert_unicode=False, assert_unicode=None, + unicode_error=None, _warn_on_bytestring=False)), + ) + + +def upgrade(migrate_engine): + # Upgrade operations go here. Don't create your own engine; + # bind migrate_engine to your metadata + meta.bind = migrate_engine + + try: + virtual_storage_arrays.create() + except Exception: + logging.info(repr(table)) + logging.exception('Exception while creating table') + raise + + +def downgrade(migrate_engine): + meta.bind = migrate_engine + + virtual_storage_arrays.drop() diff --git a/nova/db/sqlalchemy/models.py b/nova/db/sqlalchemy/models.py index 65464ece5..f8feb0b4f 100644 --- a/nova/db/sqlalchemy/models.py +++ b/nova/db/sqlalchemy/models.py @@ -352,13 +352,6 @@ class Volume(BASE, NovaBase): volume_type_id = Column(Integer) - to_vsa_id = Column(Integer, - ForeignKey('virtual_storage_arrays.id'), nullable=True) - from_vsa_id = Column(Integer, - ForeignKey('virtual_storage_arrays.id'), nullable=True) - drive_type_id = Column(Integer, - ForeignKey('drive_types.id'), nullable=True) - class VolumeMetadata(BASE, NovaBase): """Represents a metadata key/value pair for a volume""" @@ -402,38 +395,6 @@ class VolumeTypeExtraSpecs(BASE, NovaBase): 'VolumeTypeExtraSpecs.deleted == False)') -class DriveTypes(BASE, NovaBase): - """Represents the known drive types (storage media).""" - __tablename__ = 'drive_types' - - id = Column(Integer, primary_key=True, autoincrement=True) - - """ - @property - def name(self): - if self.capabilities: - return FLAGS.drive_type_template_long % \ - (self.type, str(self.size_gb), self.rpm, self.capabilities) - else: - return FLAGS.drive_type_template_short % \ - (self.type, str(self.size_gb), self.rpm) - """ - - name = Column(String(255), unique=True) - type = Column(String(255)) - size_gb = Column(Integer) - rpm = Column(String(255)) - capabilities = Column(String(255)) - - visible = Column(Boolean, default=True) - - volumes = relationship(Volume, - backref=backref('drive_type', uselist=False), - foreign_keys=id, - primaryjoin='and_(Volume.drive_type_id == ' - 'DriveTypes.id)') - - class Quota(BASE, NovaBase): """Represents a single quota override for a project. @@ -918,7 +879,9 @@ def register_models(): Network, SecurityGroup, SecurityGroupIngressRule, SecurityGroupInstanceAssociation, AuthToken, User, Project, Certificate, ConsolePool, Console, Zone, - AgentBuild, InstanceMetadata, InstanceTypeExtraSpecs, Migration) + VolumeMetadata, VolumeTypes, VolumeTypeExtraSpecs, + AgentBuild, InstanceMetadata, InstanceTypeExtraSpecs, Migration, + VirtualStorageArray) engine = create_engine(FLAGS.sql_connection, echo=False) for model in models: model.metadata.create_all(engine) diff --git a/nova/db/sqlalchemy/session.py b/nova/db/sqlalchemy/session.py index 7b717115c..643e2338e 100644 --- a/nova/db/sqlalchemy/session.py +++ b/nova/db/sqlalchemy/session.py @@ -30,9 +30,11 @@ import nova.exception import nova.flags import nova.log + FLAGS = nova.flags.FLAGS LOG = nova.log.getLogger("nova.db.sqlalchemy") + try: import MySQLdb except ImportError: -- cgit