From 7294cf0352e62b31bd026ed6e367e49f00c911e4 Mon Sep 17 00:00:00 2001 From: Gorka Eguileor Date: Wed, 23 Mar 2016 16:26:20 +0100 Subject: [PATCH] Add workers table This patch adds workers table required for the cleanup of failed services, needed for the new cleanup mechanism we'll be implementing to support Active-Active configurations. They will be used for non Active-Active configurations as well. Specs: https://review.openstack.org/236977 Implements: blueprint cinder-volume-active-active-support Change-Id: I5057a4c9071ef9ca78b680bad72fd81373473ed9 --- .../versions/076_add_workers_table.py | 52 +++++++++++++++++++ cinder/db/sqlalchemy/models.py | 37 +++++++++++++ cinder/tests/unit/test_migrations.py | 46 +++++++++------- 3 files changed, 115 insertions(+), 20 deletions(-) create mode 100644 cinder/db/sqlalchemy/migrate_repo/versions/076_add_workers_table.py diff --git a/cinder/db/sqlalchemy/migrate_repo/versions/076_add_workers_table.py b/cinder/db/sqlalchemy/migrate_repo/versions/076_add_workers_table.py new file mode 100644 index 00000000000..3480346b5a6 --- /dev/null +++ b/cinder/db/sqlalchemy/migrate_repo/versions/076_add_workers_table.py @@ -0,0 +1,52 @@ +# Copyright (c) 2016 Red Hat, 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 Boolean, Column, DateTime, Integer +from sqlalchemy import MetaData, String, Table, UniqueConstraint +from migrate.changeset.constraint import ForeignKeyConstraint + + +def upgrade(migrate_engine): + """Add workers table.""" + meta = MetaData() + meta.bind = migrate_engine + + workers = Table( + 'workers', meta, + # Inherited fields from CinderBase + Column('created_at', DateTime(timezone=False)), + Column('updated_at', DateTime(timezone=False)), + Column('deleted_at', DateTime(timezone=False)), + Column('deleted', Boolean(), default=False), + + # Workers table specific fields + Column('id', Integer, primary_key=True), + Column('resource_type', String(40), nullable=False), + Column('resource_id', String(36), nullable=False), + Column('status', String(255), nullable=False), + Column('service_id', Integer, nullable=True), + UniqueConstraint('resource_type', 'resource_id'), + + mysql_engine='InnoDB', + mysql_charset='utf8', + ) + + workers.create() + + services = Table('services', meta, autoload=True) + + ForeignKeyConstraint( + columns=[workers.c.service_id], + refcolumns=[services.c.id]).create() diff --git a/cinder/db/sqlalchemy/models.py b/cinder/db/sqlalchemy/models.py index c33ce590a72..8e0a2cec376 100644 --- a/cinder/db/sqlalchemy/models.py +++ b/cinder/db/sqlalchemy/models.py @@ -700,6 +700,43 @@ class ImageVolumeCacheEntry(BASE, models.ModelBase): last_used = Column(DateTime, default=lambda: timeutils.utcnow()) +class Worker(BASE, CinderBase): + """Represents all resources that are being worked on by a node.""" + __tablename__ = 'workers' + __table_args__ = (schema.UniqueConstraint('resource_type', 'resource_id'), + {'mysql_engine': 'InnoDB'}) + + # We want to overwrite default updated_at definition so we timestamp at + # creation as well + updated_at = Column(DateTime, default=timeutils.utcnow, + onupdate=timeutils.utcnow) + + # Id added for convenience and speed on some operations + id = Column(Integer, primary_key=True) + + # Type of the resource we are working on (Volume, Snapshot, Backup) it must + # match the Versioned Object class name. + resource_type = Column(String(40), primary_key=True, nullable=False) + # UUID of the resource we are working on + resource_id = Column(String(36), primary_key=True, nullable=False) + + # Status that should be cleaned on service failure + status = Column(String(255), nullable=False) + + # Service that is currently processing the operation + service_id = Column(Integer, nullable=True) + + # This is a flag we don't need to store in the DB as it is only used when + # we are doing the cleanup to let decorators know + cleaning = False + + service = relationship( + 'Service', + backref="workers", + foreign_keys=service_id, + primaryjoin='Worker.service_id == Service.id') + + def register_models(): """Register Models and create metadata. diff --git a/cinder/tests/unit/test_migrations.py b/cinder/tests/unit/test_migrations.py index 829b87afb7e..9a833471f0a 100644 --- a/cinder/tests/unit/test_migrations.py +++ b/cinder/tests/unit/test_migrations.py @@ -807,6 +807,13 @@ class MigrationsMixin(test_migrations.WalkVersionsMixin): self.assertIsInstance(reservations.c.allocated_id.type, self.INTEGER_TYPE) + def __check_cinderbase_fields(self, columns): + """Check fields inherited from CinderBase ORM class.""" + self.assertIsInstance(columns.created_at.type, self.TIME_TYPE) + self.assertIsInstance(columns.updated_at.type, self.TIME_TYPE) + self.assertIsInstance(columns.deleted_at.type, self.TIME_TYPE) + self.assertIsInstance(columns.deleted.type, self.BOOL_TYPE) + def _check_067(self, engine, data): iscsi_targets = db_utils.get_table(engine, 'iscsi_targets') fkey, = iscsi_targets.c.volume_id.foreign_keys @@ -843,28 +850,15 @@ class MigrationsMixin(test_migrations.WalkVersionsMixin): """Test adding cluster table and cluster_id fields.""" self.assertTrue(engine.dialect.has_table(engine.connect(), 'clusters')) clusters = db_utils.get_table(engine, 'clusters') - - # Inherited fields from CinderBase - self.assertIsInstance(clusters.c.created_at.type, - self.TIME_TYPE) - self.assertIsInstance(clusters.c.updated_at.type, - self.TIME_TYPE) - self.assertIsInstance(clusters.c.deleted_at.type, - self.TIME_TYPE) - self.assertIsInstance(clusters.c.deleted.type, - self.BOOL_TYPE) + columns = clusters.c + self.__check_cinderbase_fields(columns) # Cluster specific fields - self.assertIsInstance(clusters.c.id.type, - self.INTEGER_TYPE) - self.assertIsInstance(clusters.c.name.type, - self.VARCHAR_TYPE) - self.assertIsInstance(clusters.c.binary.type, - self.VARCHAR_TYPE) - self.assertIsInstance(clusters.c.disabled.type, - self.BOOL_TYPE) - self.assertIsInstance(clusters.c.disabled_reason.type, - self.VARCHAR_TYPE) + self.assertIsInstance(columns.id.type, self.INTEGER_TYPE) + self.assertIsInstance(columns.name.type, self.VARCHAR_TYPE) + self.assertIsInstance(columns.binary.type, self.VARCHAR_TYPE) + self.assertIsInstance(columns.disabled.type, self.BOOL_TYPE) + self.assertIsInstance(columns.disabled_reason.type, self.VARCHAR_TYPE) # Check that we have added cluster_name field to all required tables for table_name in ('services', 'consistencygroups', 'volumes'): @@ -872,6 +866,18 @@ class MigrationsMixin(test_migrations.WalkVersionsMixin): self.assertIsInstance(table.c.cluster_name.type, self.VARCHAR_TYPE) + def _check_076(self, engine, data): + workers = db_utils.get_table(engine, 'workers') + columns = workers.c + self.__check_cinderbase_fields(columns) + + # Workers specific fields + self.assertIsInstance(columns.id.type, self.INTEGER_TYPE) + self.assertIsInstance(columns.resource_type.type, self.VARCHAR_TYPE) + self.assertIsInstance(columns.resource_id.type, self.VARCHAR_TYPE) + self.assertIsInstance(columns.status.type, self.VARCHAR_TYPE) + self.assertIsInstance(columns.service_id.type, self.INTEGER_TYPE) + def test_walk_versions(self): self.walk_versions(False, False)