1# Copyright (c) 2011 X.commerce, a business unit of eBay Inc. 2# Copyright 2010 United States Government as represented by the 3# Administrator of the National Aeronautics and Space Administration. 4# Copyright 2011 Piston Cloud Computing, Inc. 5# All Rights Reserved. 6# 7# Licensed under the Apache License, Version 2.0 (the "License"); you may 8# not use this file except in compliance with the License. You may obtain 9# a copy of the License at 10# 11# http://www.apache.org/licenses/LICENSE-2.0 12# 13# Unless required by applicable law or agreed to in writing, software 14# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 15# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 16# License for the specific language governing permissions and limitations 17# under the License. 18 19""" 20SQLAlchemy models for cinder data. 21""" 22 23from oslo_config import cfg 24from oslo_db.sqlalchemy import models 25from oslo_utils import timeutils 26from sqlalchemy import and_, func, select 27from sqlalchemy import bindparam 28from sqlalchemy import Column, Integer, String, Text, schema, Index 29from sqlalchemy.ext.declarative import declarative_base 30from sqlalchemy import ForeignKey, DateTime, Boolean, UniqueConstraint 31from sqlalchemy.orm import backref, column_property, relationship, validates 32 33 34CONF = cfg.CONF 35BASE = declarative_base() 36 37 38class CinderBase(models.TimestampMixin, 39 models.ModelBase): 40 """Base class for Cinder Models.""" 41 42 __table_args__ = {'mysql_engine': 'InnoDB'} 43 44 # TODO(rpodolyaka): reuse models.SoftDeleteMixin in the next stage 45 # of implementing of BP db-cleanup 46 deleted_at = Column(DateTime) 47 deleted = Column(Boolean, default=False) 48 metadata = None 49 50 @staticmethod 51 def delete_values(): 52 return {'deleted': True, 53 'deleted_at': timeutils.utcnow()} 54 55 def delete(self, session): 56 """Delete this object.""" 57 updated_values = self.delete_values() 58 self.update(updated_values) 59 self.save(session=session) 60 return updated_values 61 62 63class Service(BASE, CinderBase): 64 """Represents a running service on a host.""" 65 66 __tablename__ = 'services' 67 id = Column(Integer, primary_key=True) 68 uuid = Column(String(36), nullable=True, index=True) 69 cluster_name = Column(String(255), nullable=True) 70 host = Column(String(255)) # , ForeignKey('hosts.id')) 71 binary = Column(String(255)) 72 # We want to overwrite default updated_at definition so we timestamp at 73 # creation as well, so we only need to check updated_at for the heartbeat 74 updated_at = Column(DateTime, default=timeutils.utcnow, 75 onupdate=timeutils.utcnow) 76 topic = Column(String(255)) 77 report_count = Column(Integer, nullable=False, default=0) 78 disabled = Column(Boolean, default=False) 79 availability_zone = Column(String(255), default='cinder') 80 disabled_reason = Column(String(255)) 81 # adding column modified_at to contain timestamp 82 # for manual enable/disable of cinder services 83 # updated_at column will now contain timestamps for 84 # periodic updates 85 modified_at = Column(DateTime) 86 87 # Version columns to support rolling upgrade. These report the max RPC API 88 # and objects versions that the manager of the service is able to support. 89 rpc_current_version = Column(String(36)) 90 object_current_version = Column(String(36)) 91 92 # replication_status can be: enabled, disabled, not-capable, error, 93 # failed-over or not-configured 94 replication_status = Column(String(36), default="not-capable") 95 active_backend_id = Column(String(255)) 96 frozen = Column(Boolean, nullable=False, default=False) 97 98 cluster = relationship('Cluster', 99 backref='services', 100 foreign_keys=cluster_name, 101 primaryjoin='and_(' 102 'Service.cluster_name == Cluster.name,' 103 'Service.deleted == False)') 104 105 106class Cluster(BASE, CinderBase): 107 """Represents a cluster of hosts.""" 108 __tablename__ = 'clusters' 109 # To remove potential races on creation we have a constraint set on name 110 # and race_preventer fields, and we set value on creation to 0, so 2 111 # clusters with the same name will fail this constraint. On deletion we 112 # change this field to the same value as the id which will be unique and 113 # will not conflict with the creation of another cluster with the same 114 # name. 115 __table_args__ = (UniqueConstraint('name', 'binary', 'race_preventer'), 116 CinderBase.__table_args__) 117 118 id = Column(Integer, primary_key=True) 119 # NOTE(geguileo): Name is constructed in the same way that Server.host but 120 # using cluster configuration option instead of host. 121 name = Column(String(255), nullable=False) 122 binary = Column(String(255), nullable=False) 123 disabled = Column(Boolean, default=False) 124 disabled_reason = Column(String(255)) 125 race_preventer = Column(Integer, nullable=False, default=0) 126 127 replication_status = Column(String(36), default="not-capable") 128 active_backend_id = Column(String(255)) 129 frozen = Column(Boolean, nullable=False, default=False) 130 131 # Last heartbeat reported by any of the services of this cluster. This is 132 # not deferred since we always want to load this field. 133 last_heartbeat = column_property( 134 select([func.max(Service.updated_at)]). 135 where(and_(Service.cluster_name == name, ~Service.deleted)). 136 correlate_except(Service), deferred=False) 137 138 # Number of existing services for this cluster 139 num_hosts = column_property( 140 select([func.count(Service.id)]). 141 where(and_(Service.cluster_name == name, ~Service.deleted)). 142 correlate_except(Service), 143 group='services_summary', deferred=True) 144 145 # Number of services that are down for this cluster 146 num_down_hosts = column_property( 147 select([func.count(Service.id)]). 148 where(and_(Service.cluster_name == name, 149 ~Service.deleted, 150 Service.updated_at < bindparam('expired'))). 151 correlate_except(Service), 152 group='services_summary', deferred=True) 153 154 @staticmethod 155 def delete_values(): 156 return {'race_preventer': Cluster.id, 157 'deleted': True, 158 'deleted_at': timeutils.utcnow()} 159 160 161class ConsistencyGroup(BASE, CinderBase): 162 """Represents a consistencygroup.""" 163 __tablename__ = 'consistencygroups' 164 id = Column(String(36), primary_key=True) 165 166 user_id = Column(String(255), nullable=False) 167 project_id = Column(String(255), nullable=False) 168 169 cluster_name = Column(String(255), nullable=True) 170 host = Column(String(255)) 171 availability_zone = Column(String(255)) 172 name = Column(String(255)) 173 description = Column(String(255)) 174 volume_type_id = Column(String(255)) 175 status = Column(String(255)) 176 cgsnapshot_id = Column(String(36)) 177 source_cgid = Column(String(36)) 178 179 180class Group(BASE, CinderBase): 181 """Represents a generic volume group.""" 182 __tablename__ = 'groups' 183 id = Column(String(36), primary_key=True) 184 185 user_id = Column(String(255), nullable=False) 186 project_id = Column(String(255), nullable=False) 187 188 cluster_name = Column(String(255)) 189 host = Column(String(255)) 190 availability_zone = Column(String(255)) 191 name = Column(String(255)) 192 description = Column(String(255)) 193 status = Column(String(255)) 194 group_type_id = Column(String(36)) 195 group_snapshot_id = Column(String(36)) 196 source_group_id = Column(String(36)) 197 198 replication_status = Column(String(255)) 199 200 201class Cgsnapshot(BASE, CinderBase): 202 """Represents a cgsnapshot.""" 203 __tablename__ = 'cgsnapshots' 204 id = Column(String(36), primary_key=True) 205 206 consistencygroup_id = Column(String(36), index=True) 207 user_id = Column(String(255), nullable=False) 208 project_id = Column(String(255), nullable=False) 209 210 name = Column(String(255)) 211 description = Column(String(255)) 212 status = Column(String(255)) 213 214 consistencygroup = relationship( 215 ConsistencyGroup, 216 backref="cgsnapshots", 217 foreign_keys=consistencygroup_id, 218 primaryjoin='Cgsnapshot.consistencygroup_id == ConsistencyGroup.id') 219 220 221class GroupSnapshot(BASE, CinderBase): 222 """Represents a group snapshot.""" 223 __tablename__ = 'group_snapshots' 224 id = Column(String(36), primary_key=True) 225 226 group_id = Column(String(36), nullable=False, index=True) 227 user_id = Column(String(255)) 228 project_id = Column(String(255)) 229 230 name = Column(String(255)) 231 description = Column(String(255)) 232 status = Column(String(255)) 233 group_type_id = Column(String(36)) 234 235 group = relationship( 236 Group, 237 backref="group_snapshots", 238 foreign_keys=group_id, 239 primaryjoin='GroupSnapshot.group_id == Group.id') 240 241 242class Volume(BASE, CinderBase): 243 """Represents a block storage device that can be attached to a vm.""" 244 __tablename__ = 'volumes' 245 __table_args__ = (Index('volumes_service_uuid_idx', 246 'deleted', 'service_uuid'), 247 CinderBase.__table_args__) 248 249 id = Column(String(36), primary_key=True) 250 _name_id = Column(String(36)) # Don't access/modify this directly! 251 252 @property 253 def name_id(self): 254 return self.id if not self._name_id else self._name_id 255 256 @name_id.setter 257 def name_id(self, value): 258 self._name_id = value 259 260 @property 261 def name(self): 262 return CONF.volume_name_template % self.name_id 263 264 ec2_id = Column(Integer) 265 user_id = Column(String(255)) 266 project_id = Column(String(255)) 267 268 snapshot_id = Column(String(36)) 269 270 cluster_name = Column(String(255), nullable=True) 271 host = Column(String(255)) # , ForeignKey('hosts.id')) 272 size = Column(Integer) 273 availability_zone = Column(String(255)) # TODO(vish): foreign key? 274 status = Column(String(255)) # TODO(vish): enum? 275 attach_status = Column(String(255)) # TODO(vish): enum 276 migration_status = Column(String(255)) 277 278 scheduled_at = Column(DateTime) 279 launched_at = Column(DateTime) 280 terminated_at = Column(DateTime) 281 282 display_name = Column(String(255)) 283 display_description = Column(String(255)) 284 285 provider_location = Column(String(255)) 286 provider_auth = Column(String(255)) 287 provider_geometry = Column(String(255)) 288 provider_id = Column(String(255)) 289 290 volume_type_id = Column(String(36)) 291 source_volid = Column(String(36)) 292 encryption_key_id = Column(String(36)) 293 294 consistencygroup_id = Column(String(36), index=True) 295 group_id = Column(String(36), index=True) 296 297 bootable = Column(Boolean, default=False) 298 multiattach = Column(Boolean, default=False) 299 300 replication_status = Column(String(255)) 301 replication_extended_status = Column(String(255)) 302 replication_driver_data = Column(String(255)) 303 304 previous_status = Column(String(255)) 305 306 consistencygroup = relationship( 307 ConsistencyGroup, 308 backref="volumes", 309 foreign_keys=consistencygroup_id, 310 primaryjoin='Volume.consistencygroup_id == ConsistencyGroup.id') 311 312 group = relationship( 313 Group, 314 backref="volumes", 315 foreign_keys=group_id, 316 primaryjoin='Volume.group_id == Group.id') 317 318 service_uuid = Column(String(36), index=True) 319 service = relationship(Service, 320 backref="volumes", 321 foreign_keys=service_uuid, 322 primaryjoin='Volume.service_uuid == Service.uuid') 323 shared_targets = Column(Boolean, default=True) # make an FK of service? 324 325 326class VolumeMetadata(BASE, CinderBase): 327 """Represents a metadata key/value pair for a volume.""" 328 __tablename__ = 'volume_metadata' 329 id = Column(Integer, primary_key=True) 330 key = Column(String(255)) 331 value = Column(String(255)) 332 volume_id = Column(String(36), ForeignKey('volumes.id'), nullable=False, 333 index=True) 334 volume = relationship(Volume, backref="volume_metadata", 335 foreign_keys=volume_id, 336 primaryjoin='and_(' 337 'VolumeMetadata.volume_id == Volume.id,' 338 'VolumeMetadata.deleted == False)') 339 340 341class VolumeAdminMetadata(BASE, CinderBase): 342 """Represents an administrator metadata key/value pair for a volume.""" 343 __tablename__ = 'volume_admin_metadata' 344 id = Column(Integer, primary_key=True) 345 key = Column(String(255)) 346 value = Column(String(255)) 347 volume_id = Column(String(36), ForeignKey('volumes.id'), nullable=False, 348 index=True) 349 volume = relationship(Volume, backref="volume_admin_metadata", 350 foreign_keys=volume_id, 351 primaryjoin='and_(' 352 'VolumeAdminMetadata.volume_id == Volume.id,' 353 'VolumeAdminMetadata.deleted == False)') 354 355 356class VolumeAttachment(BASE, CinderBase): 357 """Represents a volume attachment for a vm.""" 358 __tablename__ = 'volume_attachment' 359 id = Column(String(36), primary_key=True) 360 361 volume_id = Column(String(36), ForeignKey('volumes.id'), nullable=False, 362 index=True) 363 volume = relationship(Volume, backref="volume_attachment", 364 foreign_keys=volume_id, 365 primaryjoin='and_(' 366 'VolumeAttachment.volume_id == Volume.id,' 367 'VolumeAttachment.deleted == False)') 368 instance_uuid = Column(String(36)) 369 attached_host = Column(String(255)) 370 mountpoint = Column(String(255)) 371 attach_time = Column(DateTime) 372 detach_time = Column(DateTime) 373 attach_status = Column(String(255)) 374 attach_mode = Column(String(255)) 375 connection_info = Column(Text) 376 # Stores a serialized json dict of host connector information from brick. 377 connector = Column(Text) 378 379 380class VolumeTypes(BASE, CinderBase): 381 """Represent possible volume_types of volumes offered.""" 382 __tablename__ = "volume_types" 383 id = Column(String(36), primary_key=True) 384 name = Column(String(255)) 385 description = Column(String(255)) 386 # A reference to qos_specs entity 387 qos_specs_id = Column(String(36), 388 ForeignKey('quality_of_service_specs.id'), 389 index=True) 390 is_public = Column(Boolean, default=True) 391 volumes = relationship(Volume, 392 backref=backref('volume_type', uselist=False), 393 foreign_keys=id, 394 primaryjoin='and_(' 395 'Volume.volume_type_id == VolumeTypes.id, ' 396 'VolumeTypes.deleted == False)') 397 398 399class GroupTypes(BASE, CinderBase): 400 """Represent possible group_types of groups offered.""" 401 __tablename__ = "group_types" 402 id = Column(String(36), primary_key=True) 403 name = Column(String(255)) 404 description = Column(String(255)) 405 is_public = Column(Boolean, default=True) 406 groups = relationship(Group, 407 backref=backref('group_type', uselist=False), 408 foreign_keys=id, 409 primaryjoin='and_(' 410 'Group.group_type_id == GroupTypes.id, ' 411 'GroupTypes.deleted == False)') 412 413 414class GroupVolumeTypeMapping(BASE, CinderBase): 415 """Represent mapping between groups and volume_types.""" 416 __tablename__ = "group_volume_type_mapping" 417 id = Column(Integer, primary_key=True, nullable=False) 418 volume_type_id = Column(String(36), 419 ForeignKey('volume_types.id'), 420 nullable=False, index=True) 421 group_id = Column(String(36), 422 ForeignKey('groups.id'), 423 nullable=False, index=True) 424 425 group = relationship( 426 Group, 427 backref="volume_types", 428 foreign_keys=group_id, 429 primaryjoin='and_(' 430 'GroupVolumeTypeMapping.group_id == Group.id,' 431 'GroupVolumeTypeMapping.deleted == False)' 432 ) 433 434 435class VolumeTypeProjects(BASE, CinderBase): 436 """Represent projects associated volume_types.""" 437 __tablename__ = "volume_type_projects" 438 __table_args__ = (schema.UniqueConstraint( 439 "volume_type_id", "project_id", "deleted", 440 name="uniq_volume_type_projects0volume_type_id0project_id0deleted"), 441 CinderBase.__table_args__) 442 id = Column(Integer, primary_key=True) 443 volume_type_id = Column(String, ForeignKey('volume_types.id'), 444 nullable=False) 445 project_id = Column(String(255)) 446 deleted = Column(Integer, default=0) 447 448 volume_type = relationship( 449 VolumeTypes, 450 backref="projects", 451 foreign_keys=volume_type_id, 452 primaryjoin='and_(' 453 'VolumeTypeProjects.volume_type_id == VolumeTypes.id,' 454 'VolumeTypeProjects.deleted == 0)') 455 456 457class GroupTypeProjects(BASE, CinderBase): 458 """Represent projects associated group_types.""" 459 __tablename__ = "group_type_projects" 460 __table_args__ = (schema.UniqueConstraint( 461 "group_type_id", "project_id", "deleted", 462 name="uniq_group_type_projects0group_type_id0project_id0deleted"), 463 CinderBase.__table_args__) 464 id = Column(Integer, primary_key=True) 465 group_type_id = Column(String, ForeignKey('group_types.id'), 466 nullable=False) 467 project_id = Column(String(255)) 468 469 group_type = relationship( 470 GroupTypes, 471 backref="projects", 472 foreign_keys=group_type_id, 473 primaryjoin='and_(' 474 'GroupTypeProjects.group_type_id == GroupTypes.id,' 475 'GroupTypeProjects.deleted == False)') 476 477 478class VolumeTypeExtraSpecs(BASE, CinderBase): 479 """Represents additional specs as key/value pairs for a volume_type.""" 480 __tablename__ = 'volume_type_extra_specs' 481 id = Column(Integer, primary_key=True) 482 key = Column(String(255)) 483 value = Column(String(255)) 484 volume_type_id = Column(String(36), 485 ForeignKey('volume_types.id'), 486 nullable=False, index=True) 487 volume_type = relationship( 488 VolumeTypes, 489 backref="extra_specs", 490 foreign_keys=volume_type_id, 491 primaryjoin='and_(' 492 'VolumeTypeExtraSpecs.volume_type_id == VolumeTypes.id,' 493 'VolumeTypeExtraSpecs.deleted == False)' 494 ) 495 496 497class GroupTypeSpecs(BASE, CinderBase): 498 """Represents additional specs as key/value pairs for a group_type.""" 499 __tablename__ = 'group_type_specs' 500 id = Column(Integer, primary_key=True) 501 key = Column(String(255)) 502 value = Column(String(255)) 503 group_type_id = Column(String(36), 504 ForeignKey('group_types.id'), 505 nullable=False, index=True) 506 group_type = relationship( 507 GroupTypes, 508 backref="group_specs", 509 foreign_keys=group_type_id, 510 primaryjoin='and_(' 511 'GroupTypeSpecs.group_type_id == GroupTypes.id,' 512 'GroupTypeSpecs.deleted == False)' 513 ) 514 515 516class QualityOfServiceSpecs(BASE, CinderBase): 517 """Represents QoS specs as key/value pairs. 518 519 QoS specs is standalone entity that can be associated/disassociated 520 with volume types (one to many relation). Adjacency list relationship 521 pattern is used in this model in order to represent following hierarchical 522 data with in flat table, e.g, following structure: 523 524 .. code-block:: none 525 526 qos-specs-1 'Rate-Limit' 527 | 528 +------> consumer = 'front-end' 529 +------> total_bytes_sec = 1048576 530 +------> total_iops_sec = 500 531 532 qos-specs-2 'QoS_Level1' 533 | 534 +------> consumer = 'back-end' 535 +------> max-iops = 1000 536 +------> min-iops = 200 537 538 is represented by: 539 540 id specs_id key value 541 ------ -------- ------------- ----- 542 UUID-1 NULL QoSSpec_Name Rate-Limit 543 UUID-2 UUID-1 consumer front-end 544 UUID-3 UUID-1 total_bytes_sec 1048576 545 UUID-4 UUID-1 total_iops_sec 500 546 UUID-5 NULL QoSSpec_Name QoS_Level1 547 UUID-6 UUID-5 consumer back-end 548 UUID-7 UUID-5 max-iops 1000 549 UUID-8 UUID-5 min-iops 200 550 """ 551 __tablename__ = 'quality_of_service_specs' 552 id = Column(String(36), primary_key=True) 553 specs_id = Column(String(36), ForeignKey(id), index=True) 554 key = Column(String(255)) 555 value = Column(String(255)) 556 557 specs = relationship( 558 "QualityOfServiceSpecs", 559 cascade="all, delete-orphan", 560 backref=backref("qos_spec", remote_side=id), 561 ) 562 563 vol_types = relationship( 564 VolumeTypes, 565 backref=backref('qos_specs'), 566 foreign_keys=id, 567 primaryjoin='and_(' 568 'or_(VolumeTypes.qos_specs_id == ' 569 'QualityOfServiceSpecs.id,' 570 'VolumeTypes.qos_specs_id == ' 571 'QualityOfServiceSpecs.specs_id),' 572 'QualityOfServiceSpecs.deleted == False)') 573 574 575class VolumeGlanceMetadata(BASE, CinderBase): 576 """Glance metadata for a bootable volume.""" 577 __tablename__ = 'volume_glance_metadata' 578 id = Column(Integer, primary_key=True, nullable=False) 579 volume_id = Column(String(36), ForeignKey('volumes.id'), index=True) 580 snapshot_id = Column(String(36), ForeignKey('snapshots.id'), index=True) 581 key = Column(String(255)) 582 value = Column(Text) 583 volume = relationship(Volume, backref="volume_glance_metadata", 584 foreign_keys=volume_id, 585 primaryjoin='and_(' 586 'VolumeGlanceMetadata.volume_id == Volume.id,' 587 'VolumeGlanceMetadata.deleted == False)') 588 589 590class Quota(BASE, CinderBase): 591 """Represents a single quota override for a project. 592 593 If there is no row for a given project id and resource, then the 594 default for the quota class is used. If there is no row for a 595 given quota class and resource, then the default for the 596 deployment is used. If the row is present but the hard limit is 597 Null, then the resource is unlimited. 598 """ 599 600 __tablename__ = 'quotas' 601 id = Column(Integer, primary_key=True) 602 603 project_id = Column(String(255), index=True) 604 605 resource = Column(String(255)) 606 hard_limit = Column(Integer, nullable=True) 607 allocated = Column(Integer, default=0) 608 609 610class QuotaClass(BASE, CinderBase): 611 """Represents a single quota override for a quota class. 612 613 If there is no row for a given quota class and resource, then the 614 default for the deployment is used. If the row is present but the 615 hard limit is Null, then the resource is unlimited. 616 """ 617 618 __tablename__ = 'quota_classes' 619 id = Column(Integer, primary_key=True) 620 621 class_name = Column(String(255), index=True) 622 623 resource = Column(String(255)) 624 hard_limit = Column(Integer, nullable=True) 625 626 627class QuotaUsage(BASE, CinderBase): 628 """Represents the current usage for a given resource.""" 629 630 __tablename__ = 'quota_usages' 631 id = Column(Integer, primary_key=True) 632 633 project_id = Column(String(255), index=True) 634 resource = Column(String(255), index=True) 635 636 in_use = Column(Integer) 637 reserved = Column(Integer) 638 639 @property 640 def total(self): 641 return self.in_use + self.reserved 642 643 until_refresh = Column(Integer, nullable=True) 644 645 646class Reservation(BASE, CinderBase): 647 """Represents a resource reservation for quotas.""" 648 649 __tablename__ = 'reservations' 650 __table_args__ = (Index('reservations_deleted_expire_idx', 651 'deleted', 'expire'), 652 Index('reservations_deleted_uuid_idx', 653 'deleted', 'uuid'), 654 CinderBase.__table_args__) 655 656 id = Column(Integer, primary_key=True) 657 uuid = Column(String(36), nullable=False) 658 659 usage_id = Column(Integer, ForeignKey('quota_usages.id'), nullable=True, 660 index=True) 661 allocated_id = Column(Integer, ForeignKey('quotas.id'), nullable=True, 662 index=True) 663 664 project_id = Column(String(255), index=True) 665 resource = Column(String(255)) 666 667 delta = Column(Integer) 668 expire = Column(DateTime, nullable=False) 669 670 usage = relationship( 671 "QuotaUsage", 672 foreign_keys=usage_id, 673 primaryjoin='and_(Reservation.usage_id == QuotaUsage.id,' 674 'QuotaUsage.deleted == 0)') 675 quota = relationship( 676 "Quota", 677 foreign_keys=allocated_id, 678 primaryjoin='and_(Reservation.allocated_id == Quota.id)') 679 680 681class Snapshot(BASE, CinderBase): 682 """Represents a snapshot of volume.""" 683 __tablename__ = 'snapshots' 684 id = Column(String(36), primary_key=True) 685 686 @property 687 def name(self): 688 return CONF.snapshot_name_template % self.id 689 690 @property 691 def volume_name(self): 692 return self.volume.name # pylint: disable=E1101 693 694 user_id = Column(String(255)) 695 project_id = Column(String(255)) 696 697 volume_id = Column(String(36), index=True) 698 cgsnapshot_id = Column(String(36), index=True) 699 group_snapshot_id = Column(String(36), index=True) 700 status = Column(String(255)) 701 progress = Column(String(255)) 702 volume_size = Column(Integer) 703 704 display_name = Column(String(255)) 705 display_description = Column(String(255)) 706 707 encryption_key_id = Column(String(36)) 708 volume_type_id = Column(String(36)) 709 710 provider_location = Column(String(255)) 711 provider_id = Column(String(255)) 712 provider_auth = Column(String(255)) 713 714 volume = relationship(Volume, backref="snapshots", 715 foreign_keys=volume_id, 716 primaryjoin='Snapshot.volume_id == Volume.id') 717 718 cgsnapshot = relationship( 719 Cgsnapshot, 720 backref="snapshots", 721 foreign_keys=cgsnapshot_id, 722 primaryjoin='Snapshot.cgsnapshot_id == Cgsnapshot.id') 723 724 group_snapshot = relationship( 725 GroupSnapshot, 726 backref="snapshots", 727 foreign_keys=group_snapshot_id, 728 primaryjoin='Snapshot.group_snapshot_id == GroupSnapshot.id') 729 730 731class SnapshotMetadata(BASE, CinderBase): 732 """Represents a metadata key/value pair for a snapshot.""" 733 __tablename__ = 'snapshot_metadata' 734 id = Column(Integer, primary_key=True) 735 key = Column(String(255)) 736 value = Column(String(255)) 737 snapshot_id = Column(String(36), 738 ForeignKey('snapshots.id'), 739 nullable=False, index=True) 740 snapshot = relationship(Snapshot, backref="snapshot_metadata", 741 foreign_keys=snapshot_id, 742 primaryjoin='and_(' 743 'SnapshotMetadata.snapshot_id == Snapshot.id,' 744 'SnapshotMetadata.deleted == False)') 745 746 747class Backup(BASE, CinderBase): 748 """Represents a backup of a volume to Swift.""" 749 __tablename__ = 'backups' 750 id = Column(String(36), primary_key=True) 751 752 @property 753 def name(self): 754 return CONF.backup_name_template % self.id 755 756 user_id = Column(String(255), nullable=False) 757 project_id = Column(String(255), nullable=False) 758 759 volume_id = Column(String(36), nullable=False) 760 host = Column(String(255)) 761 availability_zone = Column(String(255)) 762 display_name = Column(String(255)) 763 display_description = Column(String(255)) 764 container = Column(String(255)) 765 parent_id = Column(String(36)) 766 status = Column(String(255)) 767 fail_reason = Column(String(255)) 768 service_metadata = Column(String(255)) 769 service = Column(String(255)) 770 size = Column(Integer) 771 object_count = Column(Integer) 772 temp_volume_id = Column(String(36)) 773 temp_snapshot_id = Column(String(36)) 774 num_dependent_backups = Column(Integer) 775 snapshot_id = Column(String(36)) 776 data_timestamp = Column(DateTime) 777 restore_volume_id = Column(String(36)) 778 encryption_key_id = Column(String(36)) 779 780 @validates('fail_reason') 781 def validate_fail_reason(self, key, fail_reason): 782 return fail_reason and fail_reason[:255] or '' 783 784 785class BackupMetadata(BASE, CinderBase): 786 """Represents a metadata key/value pair for a backup.""" 787 __tablename__ = 'backup_metadata' 788 id = Column(Integer, primary_key=True) 789 key = Column(String(255)) 790 value = Column(String(255)) 791 backup_id = Column(String(36), ForeignKey('backups.id'), nullable=False, 792 index=True) 793 backup = relationship(Backup, backref="backup_metadata", 794 foreign_keys=backup_id, 795 primaryjoin='and_(' 796 'BackupMetadata.backup_id == Backup.id,' 797 'BackupMetadata.deleted == False)') 798 799 800class Encryption(BASE, CinderBase): 801 """Represents encryption requirement for a volume type. 802 803 Encryption here is a set of performance characteristics describing 804 cipher, provider, and key_size for a certain volume type. 805 """ 806 807 __tablename__ = 'encryption' 808 encryption_id = Column(String(36), primary_key=True) 809 cipher = Column(String(255)) 810 key_size = Column(Integer) 811 provider = Column(String(255)) 812 control_location = Column(String(255)) 813 volume_type_id = Column(String(36), ForeignKey('volume_types.id')) 814 volume_type = relationship( 815 VolumeTypes, 816 backref="encryption", 817 foreign_keys=volume_type_id, 818 primaryjoin='and_(' 819 'Encryption.volume_type_id == VolumeTypes.id,' 820 'Encryption.deleted == False)' 821 ) 822 823 824class Transfer(BASE, CinderBase): 825 """Represents a volume transfer request.""" 826 __tablename__ = 'transfers' 827 id = Column(String(36), primary_key=True) 828 volume_id = Column(String(36), ForeignKey('volumes.id'), index=True) 829 display_name = Column(String(255)) 830 salt = Column(String(255)) 831 crypt_hash = Column(String(255)) 832 expires_at = Column(DateTime) 833 volume = relationship(Volume, backref="transfer", 834 foreign_keys=volume_id, 835 primaryjoin='and_(' 836 'Transfer.volume_id == Volume.id,' 837 'Transfer.deleted == False)') 838 839 840class DriverInitiatorData(BASE, models.TimestampMixin, models.ModelBase): 841 """Represents private key-value pair specific an initiator for drivers""" 842 __tablename__ = 'driver_initiator_data' 843 __table_args__ = ( 844 schema.UniqueConstraint("initiator", "namespace", "key"), 845 CinderBase.__table_args__) 846 847 id = Column(Integer, primary_key=True, nullable=False) 848 initiator = Column(String(255), index=True, nullable=False) 849 namespace = Column(String(255), nullable=False) 850 key = Column(String(255), nullable=False) 851 value = Column(String(255)) 852 853 854class Message(BASE, CinderBase): 855 """Represents a message""" 856 __tablename__ = 'messages' 857 id = Column(String(36), primary_key=True, nullable=False) 858 project_id = Column(String(255), nullable=False) 859 # Info/Error/Warning. 860 message_level = Column(String(255), nullable=False) 861 request_id = Column(String(255), nullable=True) 862 resource_type = Column(String(255)) 863 # The UUID of the related resource. 864 resource_uuid = Column(String(36), nullable=True) 865 # Operation specific event ID. 866 event_id = Column(String(255), nullable=False) 867 # Message detail ID. 868 detail_id = Column(String(10), nullable=True) 869 # Operation specific action. 870 action_id = Column(String(10), nullable=True) 871 # After this time the message may no longer exist 872 expires_at = Column(DateTime, nullable=True, index=True) 873 874 875class ImageVolumeCacheEntry(BASE, models.ModelBase): 876 """Represents an image volume cache entry""" 877 __tablename__ = 'image_volume_cache_entries' 878 879 id = Column(Integer, primary_key=True, nullable=False) 880 host = Column(String(255), index=True, nullable=False) 881 cluster_name = Column(String(255), nullable=True) 882 image_id = Column(String(36), index=True, nullable=False) 883 image_updated_at = Column(DateTime, nullable=False) 884 volume_id = Column(String(36), nullable=False) 885 size = Column(Integer, nullable=False) 886 last_used = Column(DateTime, default=lambda: timeutils.utcnow()) 887 888 889class Worker(BASE, CinderBase): 890 """Represents all resources that are being worked on by a node.""" 891 __tablename__ = 'workers' 892 __table_args__ = (schema.UniqueConstraint('resource_type', 'resource_id'), 893 CinderBase.__table_args__) 894 895 # We want to overwrite default updated_at definition so we timestamp at 896 # creation as well 897 updated_at = Column(DateTime, default=timeutils.utcnow, 898 onupdate=timeutils.utcnow) 899 900 # Id added for convenience and speed on some operations 901 id = Column(Integer, primary_key=True, autoincrement=True) 902 903 # Type of the resource we are working on (Volume, Snapshot, Backup) it must 904 # match the Versioned Object class name. 905 resource_type = Column(String(40), primary_key=True, nullable=False) 906 # UUID of the resource we are working on 907 resource_id = Column(String(36), primary_key=True, nullable=False) 908 909 # Status that should be cleaned on service failure 910 status = Column(String(255), nullable=False) 911 912 # Service that is currently processing the operation 913 service_id = Column(Integer, nullable=True, index=True) 914 915 # To prevent claiming and updating races 916 race_preventer = Column(Integer, nullable=False, default=0) 917 918 # This is a flag we don't need to store in the DB as it is only used when 919 # we are doing the cleanup to let decorators know 920 cleaning = False 921 922 service = relationship( 923 'Service', 924 backref="workers", 925 foreign_keys=service_id, 926 primaryjoin='Worker.service_id == Service.id') 927 928 929class AttachmentSpecs(BASE, CinderBase): 930 """Represents attachment specs as k/v pairs for a volume_attachment. 931 932 DO NOT USE - NOTHING SHOULD WRITE NEW DATA TO THIS TABLE 933 934 The volume_attachment.connector column should be used instead. 935 """ 936 937 __tablename__ = 'attachment_specs' 938 id = Column(Integer, primary_key=True) 939 key = Column(String(255)) 940 value = Column(String(255)) 941 attachment_id = Column(String(36), ForeignKey('volume_attachment.id'), 942 nullable=False, index=True) 943 volume_attachment = relationship( 944 VolumeAttachment, 945 backref="attachment_specs", 946 foreign_keys=attachment_id, 947 primaryjoin='and_(' 948 'AttachmentSpecs.attachment_id == VolumeAttachment.id,' 949 'AttachmentSpecs.deleted == False)' 950 ) 951