Coverage for nova/db/main/models.py: 99%
460 statements
« prev ^ index » next coverage.py v7.6.12, created at 2025-04-17 15:08 +0000
« prev ^ index » next coverage.py v7.6.12, created at 2025-04-17 15:08 +0000
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"""
19SQLAlchemy models for nova data.
20"""
22from oslo_config import cfg
23from oslo_db.sqlalchemy import models
24from oslo_utils import timeutils
25import sqlalchemy as sa
26import sqlalchemy.dialects.mysql
27from sqlalchemy.ext import declarative
28from sqlalchemy import orm
29from sqlalchemy import schema
31from nova.db import types
33CONF = cfg.CONF
35# NOTE(stephenfin): This is a list of fields that have been removed from
36# various SQLAlchemy models but which still exist in the underlying tables. Our
37# upgrade policy dictates that we remove fields from models at least one cycle
38# before we remove the column from the underlying table. Not doing so would
39# prevent us from applying the new database schema before rolling out any of
40# the new code since the old code could attempt to access data in the removed
41# columns. Alembic identifies this temporary mismatch between the models and
42# underlying tables and attempts to resolve it. Tell it instead to ignore these
43# until we're ready to remove them ourselves.
44REMOVED_COLUMNS = {
45 ('instances', 'internal_id'),
46 ('instance_extra', 'vpmems'),
47}
49# NOTE(stephenfin): A list of foreign key constraints that were removed when
50# the column they were covering was removed.
51REMOVED_FKEYS = []
53# NOTE(stephenfin): A list of entire models that have been removed.
54REMOVED_TABLES = {
55 # Tables that were moved to the API database in Newton. The models
56 # were removed in Y and the tables can be dropped in Z or later
57 'aggregate_hosts',
58 'aggregate_metadata',
59 'aggregates',
60 'allocations',
61 'instance_group_member',
62 'instance_group_policy',
63 'instance_groups',
64 'instance_type_extra_specs',
65 'instance_type_projects',
66 'instance_types',
67 'inventories',
68 'key_pairs',
69 'resource_provider_aggregates',
70 'resource_providers',
72 # Tables for the removed XenAPI virt driver. The models were
73 # removed in Y and the tables can be dropped in Z or later
74 'agent_builds',
75 'bw_usage_cache',
76 'console_pools',
77 'consoles',
79 # Tables for the removed cells v1 feature. The model was removed in
80 # Y and the table can be dropped in Z or later
81 'cells',
83 # Tables for the removed volume snapshot feature. The model was
84 # removed in Y and the table can be dropped in Z or later
85 'snapshots',
87 # Tables for the removed in-tree EC2 API. The models were removed
88 # in Y and the table can be dropped in Z or later
89 'snapshot_id_mappings',
90 'volume_id_mappings',
92 # Tables for the removed nova-network feature. The models were
93 # removed in Y and the tables can be dropped in Z or later
94 'dns_domains',
95 'fixed_ips',
96 'floating_ips',
97 'networks',
98 'provider_fw_rules',
99 'security_group_default_rules',
100}
102# we don't configure 'cls' since we have models that don't use the
103# TimestampMixin
104BASE = declarative.declarative_base()
107class NovaBase(models.TimestampMixin, models.ModelBase):
109 def __copy__(self):
110 """Implement a safe copy.copy().
112 SQLAlchemy-mapped objects travel with an object
113 called an InstanceState, which is pegged to that object
114 specifically and tracks everything about that object. It's
115 critical within all attribute operations, including gets
116 and deferred loading. This object definitely cannot be
117 shared among two instances, and must be handled.
119 The copy routine here makes use of session.merge() which
120 already essentially implements a "copy" style of operation,
121 which produces a new instance with a new InstanceState and copies
122 all the data along mapped attributes without using any SQL.
124 The mode we are using here has the caveat that the given object
125 must be "clean", e.g. that it has no database-loaded state
126 that has been updated and not flushed. This is a good thing,
127 as creating a copy of an object including non-flushed, pending
128 database state is probably not a good idea; neither represents
129 what the actual row looks like, and only one should be flushed.
131 """
132 session = orm.Session()
134 copy = session.merge(self, load=False)
135 session.expunge(copy)
136 return copy
139class Service(BASE, NovaBase, models.SoftDeleteMixin):
140 """Represents a running service on a host."""
142 __tablename__ = 'services'
143 __table_args__ = (
144 schema.UniqueConstraint("host", "topic", "deleted",
145 name="uniq_services0host0topic0deleted"),
146 schema.UniqueConstraint("host", "binary", "deleted",
147 name="uniq_services0host0binary0deleted"),
148 sa.Index('services_uuid_idx', 'uuid', unique=True),
149 )
151 id = sa.Column(sa.Integer, primary_key=True)
152 uuid = sa.Column(sa.String(36), nullable=True)
153 host = sa.Column(sa.String(255))
154 binary = sa.Column(sa.String(255))
155 topic = sa.Column(sa.String(255))
156 report_count = sa.Column(sa.Integer, nullable=False, default=0)
157 disabled = sa.Column(sa.Boolean, default=False)
158 disabled_reason = sa.Column(sa.String(255))
159 last_seen_up = sa.Column(sa.DateTime, nullable=True)
160 forced_down = sa.Column(sa.Boolean, default=False)
161 version = sa.Column(sa.Integer, default=0)
163 instance = orm.relationship(
164 'Instance',
165 back_populates='services',
166 primaryjoin='and_(Service.host == Instance.host,'
167 'Service.binary == "nova-compute",'
168 'Instance.deleted == 0)',
169 foreign_keys=host,
170 )
173class ComputeNode(BASE, NovaBase, models.SoftDeleteMixin):
174 """Represents a running compute service on a host."""
176 __tablename__ = 'compute_nodes'
177 __table_args__ = (
178 sa.Index('compute_nodes_uuid_idx', 'uuid', unique=True),
179 schema.UniqueConstraint(
180 'host', 'hypervisor_hostname', 'deleted',
181 name="uniq_compute_nodes0host0hypervisor_hostname0deleted"),
182 )
183 id = sa.Column(sa.Integer, primary_key=True)
184 service_id = sa.Column(sa.Integer, nullable=True)
186 # FIXME(sbauza: Host field is nullable because some old Juno compute nodes
187 # can still report stats from an old ResourceTracker without setting this
188 # field.
189 # This field has to be set non-nullable in a later cycle (probably Lxxx)
190 # once we are sure that all compute nodes in production report it.
191 host = sa.Column(sa.String(255), nullable=True)
192 uuid = sa.Column(sa.String(36), nullable=True)
193 vcpus = sa.Column(sa.Integer, nullable=False)
194 memory_mb = sa.Column(sa.Integer, nullable=False)
195 local_gb = sa.Column(sa.Integer, nullable=False)
196 vcpus_used = sa.Column(sa.Integer, nullable=False)
197 memory_mb_used = sa.Column(sa.Integer, nullable=False)
198 local_gb_used = sa.Column(sa.Integer, nullable=False)
199 hypervisor_type = sa.Column(types.MediumText(), nullable=False)
200 hypervisor_version = sa.Column(sa.Integer, nullable=False)
201 hypervisor_hostname = sa.Column(sa.String(255))
203 # Free Ram, amount of activity (resize, migration, boot, etc) and
204 # the number of running VM's are a good starting point for what's
205 # important when making scheduling decisions.
206 free_ram_mb = sa.Column(sa.Integer)
207 free_disk_gb = sa.Column(sa.Integer)
208 current_workload = sa.Column(sa.Integer)
209 running_vms = sa.Column(sa.Integer)
211 # Note(masumotok): Expected Strings example:
212 #
213 # '{"arch":"x86_64",
214 # "model":"Nehalem",
215 # "topology":{"sockets":1, "threads":2, "cores":3},
216 # "features":["tdtscp", "xtpr"]}'
217 #
218 # Points are "json translatable" and it must have all dictionary keys
219 # above, since it is copied from <cpu> tag of getCapabilities()
220 # (See libvirt.virtConnection).
221 cpu_info = sa.Column(types.MediumText(), nullable=False)
222 disk_available_least = sa.Column(sa.Integer)
223 host_ip = sa.Column(types.IPAddress())
224 supported_instances = sa.Column(sa.Text)
225 metrics = sa.Column(sa.Text)
227 # Note(yongli): json string PCI Stats
228 # '[{"vendor_id":"8086", "product_id":"1234", "count":3 }, ...]'
229 pci_stats = sa.Column(sa.Text)
231 # extra_resources is a json string containing arbitrary
232 # data about additional resources.
233 extra_resources = sa.Column(sa.Text)
235 # json-encode string containing compute node statistics
236 stats = sa.Column(sa.Text, default='{}')
238 # json-encoded dict that contains NUMA topology as generated by
239 # objects.NUMATopology._to_json()
240 numa_topology = sa.Column(sa.Text)
242 # allocation ratios provided by the RT
243 ram_allocation_ratio = sa.Column(sa.Float, nullable=True)
244 cpu_allocation_ratio = sa.Column(sa.Float, nullable=True)
245 disk_allocation_ratio = sa.Column(sa.Float, nullable=True)
246 mapped = sa.Column(sa.Integer, nullable=True, default=0)
249class Certificate(BASE, NovaBase, models.SoftDeleteMixin):
250 """Represents a x509 certificate."""
251 __tablename__ = 'certificates'
252 __table_args__ = (
253 sa.Index(
254 'certificates_project_id_deleted_idx', 'project_id', 'deleted',
255 ),
256 sa.Index('certificates_user_id_deleted_idx', 'user_id', 'deleted')
257 )
258 id = sa.Column(sa.Integer, primary_key=True)
260 user_id = sa.Column(sa.String(255))
261 project_id = sa.Column(sa.String(255))
262 file_name = sa.Column(sa.String(255))
265class Instance(BASE, NovaBase, models.SoftDeleteMixin):
266 """Represents a guest VM."""
267 __tablename__ = 'instances'
268 __table_args__ = (
269 sa.Index('instances_project_id_idx', 'project_id'),
270 sa.Index('instances_project_id_deleted_idx',
271 'project_id', 'deleted'),
272 sa.Index('instances_reservation_id_idx',
273 'reservation_id'),
274 sa.Index('instances_terminated_at_launched_at_idx',
275 'terminated_at', 'launched_at'),
276 sa.Index('instances_uuid_deleted_idx',
277 'uuid', 'deleted'),
278 sa.Index('instances_task_state_updated_at_idx',
279 'task_state', 'updated_at'),
280 sa.Index('instances_host_node_deleted_idx',
281 'host', 'node', 'deleted'),
282 sa.Index('instances_host_deleted_cleaned_idx',
283 'host', 'deleted', 'cleaned'),
284 sa.Index('instances_deleted_created_at_idx',
285 'deleted', 'created_at'),
286 sa.Index('instances_updated_at_project_id_idx',
287 'updated_at', 'project_id'),
288 sa.Index('instances_compute_id_deleted_idx',
289 'compute_id', 'deleted'),
290 schema.UniqueConstraint('uuid', name='uniq_instances0uuid'),
291 )
292 injected_files = []
294 id = sa.Column(sa.Integer, primary_key=True, autoincrement=True)
296 @property
297 def name(self):
298 try:
299 base_name = CONF.instance_name_template % self.id
300 except TypeError:
301 # Support templates like "uuid-%(uuid)s", etc.
302 info = {}
303 # NOTE(russellb): Don't use self.iteritems() here, as it will
304 # result in infinite recursion on the name property.
305 for column in iter(orm.object_mapper(self).columns):
306 key = column.name
307 # prevent recursion if someone specifies %(name)s
308 # %(name)s will not be valid.
309 if key == 'name': 309 ↛ 310line 309 didn't jump to line 310 because the condition on line 309 was never true
310 continue
311 info[key] = self[key]
312 try:
313 base_name = CONF.instance_name_template % info
314 except KeyError:
315 base_name = self.uuid
316 return base_name
318 @property
319 def _extra_keys(self):
320 return ['name']
322 user_id = sa.Column(sa.String(255))
323 project_id = sa.Column(sa.String(255))
325 image_ref = sa.Column(sa.String(255))
326 kernel_id = sa.Column(sa.String(255))
327 ramdisk_id = sa.Column(sa.String(255))
328 hostname = sa.Column(sa.String(255))
330 launch_index = sa.Column(sa.Integer)
331 key_name = sa.Column(sa.String(255))
332 key_data = sa.Column(types.MediumText())
334 power_state = sa.Column(sa.Integer)
335 vm_state = sa.Column(sa.String(255))
336 task_state = sa.Column(sa.String(255))
338 memory_mb = sa.Column(sa.Integer)
339 vcpus = sa.Column(sa.Integer)
340 root_gb = sa.Column(sa.Integer)
341 ephemeral_gb = sa.Column(sa.Integer)
342 ephemeral_key_uuid = sa.Column(sa.String(36))
344 # This is not related to hostname, above. It refers
345 # to the nova node.
346 host = sa.Column(sa.String(255))
347 # To identify the "ComputeNode" which the instance resides in.
348 # This equals to ComputeNode.hypervisor_hostname.
349 node = sa.Column(sa.String(255))
350 # This identifies the ComputeNode object that this instance resides
351 # on and should be equivalent to the one referenced by the 'node'
352 # field above.
353 compute_id = sa.Column(sa.BigInteger())
355 # *not* flavorid, this is the internal primary_key
356 instance_type_id = sa.Column(sa.Integer)
358 user_data = sa.Column(types.MediumText())
360 reservation_id = sa.Column(sa.String(255))
362 launched_at = sa.Column(sa.DateTime)
363 terminated_at = sa.Column(sa.DateTime)
365 # This always refers to the availability_zone kwarg passed in /servers and
366 # provided as an API option, not at all related to the host AZ the instance
367 # belongs to.
368 availability_zone = sa.Column(sa.String(255))
370 # User editable field for display in user-facing UIs
371 display_name = sa.Column(sa.String(255))
372 display_description = sa.Column(sa.String(255))
374 # To remember on which host an instance booted.
375 # An instance may have moved to another host by live migration.
376 launched_on = sa.Column(types.MediumText())
378 # locked is superseded by locked_by and locked is not really
379 # necessary but still used in API code so it remains.
380 locked = sa.Column(sa.Boolean)
381 locked_by = sa.Column(
382 sa.Enum('owner', 'admin', name='instances0locked_by'))
384 os_type = sa.Column(sa.String(255))
385 architecture = sa.Column(sa.String(255))
386 vm_mode = sa.Column(sa.String(255))
387 uuid = sa.Column(sa.String(36), nullable=False)
389 root_device_name = sa.Column(sa.String(255))
390 default_ephemeral_device = sa.Column(sa.String(255))
391 default_swap_device = sa.Column(sa.String(255))
392 config_drive = sa.Column(sa.String(255))
394 # User editable field meant to represent what ip should be used
395 # to connect to the instance
396 access_ip_v4 = sa.Column(types.IPAddress())
397 access_ip_v6 = sa.Column(types.IPAddress())
399 auto_disk_config = sa.Column(sa.Boolean())
400 progress = sa.Column(sa.Integer)
402 # EC2 instance_initiated_shutdown_terminate
403 # True: -> 'terminate'
404 # False: -> 'stop'
405 # Note(maoy): currently Nova will always stop instead of terminate
406 # no matter what the flag says. So we set the default to False.
407 shutdown_terminate = sa.Column(sa.Boolean(), default=False)
409 # EC2 disable_api_termination
410 disable_terminate = sa.Column(sa.Boolean(), default=False)
412 # OpenStack compute cell name. This will only be set at the top of
413 # the cells tree and it'll be a full cell name such as 'api!hop1!hop2'
414 # TODO(stephenfin): Remove this
415 cell_name = sa.Column(sa.String(255))
417 # NOTE(pumaranikar): internal_id attribute is no longer used (bug 1441242)
418 # Hence, removing from object layer in current release (Ocata) and will
419 # treated as deprecated. The column can be removed from schema with
420 # a migration at the start of next release.
421 # internal_id = sa.Column(sa.Integer)
423 # Records whether an instance has been deleted from disk
424 cleaned = sa.Column(sa.Integer, default=0)
426 hidden = sa.Column(sa.Boolean, default=False)
428 block_device_mapping = orm.relationship(
429 'BlockDeviceMapping',
430 back_populates='instance',
431 primaryjoin=(
432 'and_(BlockDeviceMapping.instance_uuid == Instance.uuid, '
433 'BlockDeviceMapping.deleted == 0)'
434 ),
435 )
436 console_auth_tokens = orm.relationship(
437 'ConsoleAuthToken',
438 back_populates='instance',
439 foreign_keys='ConsoleAuthToken.instance_uuid',
440 primaryjoin=(
441 'and_(Instance.uuid == ConsoleAuthToken.instance_uuid,'
442 'Instance.deleted == 0)'
443 ),
444 )
445 extra = orm.relationship(
446 'InstanceExtra',
447 back_populates='instance',
448 uselist=False,
449 )
450 info_cache = orm.relationship(
451 'InstanceInfoCache',
452 back_populates='instance',
453 uselist=False,
454 )
455 pci_devices = orm.relationship(
456 'PciDevice',
457 back_populates='instance',
458 foreign_keys='PciDevice.instance_uuid',
459 primaryjoin=(
460 'and_(Instance.uuid == PciDevice.instance_uuid,'
461 'PciDevice.deleted == 0)'
462 ),
463 )
464 services = orm.relationship(
465 'Service',
466 back_populates='instance',
467 primaryjoin=(
468 'and_(Instance.host == Service.host,'
469 'Service.binary == "nova-compute",'
470 'Instance.deleted == 0)'
471 ),
472 foreign_keys='Service.host',
473 )
474 security_groups = orm.relationship(
475 'SecurityGroup',
476 secondary='security_group_instance_association',
477 back_populates='instances',
478 primaryjoin=(
479 'and_('
480 'SecurityGroupInstanceAssociation.instance_uuid == Instance.uuid,'
481 # (anthony) the condition below shouldn't be necessary now that the
482 # association is being marked as deleted. However, removing this
483 # may cause existing deployments to choke, so I'm leaving it
484 'Instance.deleted == 0)'
485 ),
486 secondaryjoin=(
487 'and_('
488 'SecurityGroup.id == SecurityGroupInstanceAssociation.security_group_id,' # noqa: E501
489 'SecurityGroupInstanceAssociation.deleted == 0,'
490 'SecurityGroup.deleted == 0)'
491 ),
492 )
493 system_metadata = orm.relationship(
494 'InstanceSystemMetadata',
495 back_populates='instance',
496 )
497 tags = orm.relationship(
498 'Tag',
499 back_populates='instance',
500 primaryjoin=(
501 'and_(Instance.uuid == Tag.resource_id,Instance.deleted == 0)'
502 ),
503 foreign_keys='Tag.resource_id',
504 )
507# NOTE(stephenfin): https://github.com/sqlalchemy/sqlalchemy/discussions/8619
508Instance.metadata = orm.relationship(
509 'InstanceMetadata',
510 back_populates='instance',
511 foreign_keys='InstanceMetadata.instance_uuid',
512 primaryjoin=(
513 'and_(Instance.uuid == InstanceMetadata.instance_uuid,'
514 'InstanceMetadata.deleted == 0)'
515 ),
516)
519class InstanceInfoCache(BASE, NovaBase, models.SoftDeleteMixin):
520 """Represents a cache of information about an instance
521 """
522 __tablename__ = 'instance_info_caches'
523 __table_args__ = (
524 schema.UniqueConstraint(
525 "instance_uuid",
526 name="uniq_instance_info_caches0instance_uuid"),)
527 id = sa.Column(sa.Integer, primary_key=True, autoincrement=True)
529 # text column used for storing a json object of network data for api
530 network_info = sa.Column(types.MediumText())
532 instance_uuid = sa.Column(sa.String(36), sa.ForeignKey('instances.uuid'),
533 nullable=False)
534 instance = orm.relationship(Instance,
535 back_populates='info_cache',
536 foreign_keys=instance_uuid,
537 primaryjoin=instance_uuid == Instance.uuid)
540class InstanceExtra(BASE, NovaBase, models.SoftDeleteMixin):
541 __tablename__ = 'instance_extra'
542 __table_args__ = (
543 sa.Index('instance_extra_idx', 'instance_uuid'),)
544 id = sa.Column(sa.Integer, primary_key=True, autoincrement=True)
545 instance_uuid = sa.Column(sa.String(36), sa.ForeignKey('instances.uuid'),
546 nullable=False)
547 device_metadata = orm.deferred(sa.Column(sa.Text))
548 numa_topology = orm.deferred(sa.Column(sa.Text))
549 pci_requests = orm.deferred(sa.Column(sa.Text))
550 flavor = orm.deferred(sa.Column(sa.Text))
551 vcpu_model = orm.deferred(sa.Column(sa.Text))
552 migration_context = orm.deferred(sa.Column(sa.Text))
553 keypairs = orm.deferred(sa.Column(sa.Text))
554 trusted_certs = orm.deferred(sa.Column(sa.Text))
555 # NOTE(Luyao): 'vpmems' is still in the database
556 # and can be removed in the future release.
557 resources = orm.deferred(sa.Column(sa.Text))
558 instance = orm.relationship(Instance,
559 back_populates='extra',
560 foreign_keys=instance_uuid,
561 primaryjoin=instance_uuid == Instance.uuid)
564class Quota(BASE, NovaBase, models.SoftDeleteMixin):
565 """Represents a single quota override for a project.
567 If there is no row for a given project id and resource, then the
568 default for the quota class is used. If there is no row for a
569 given quota class and resource, then the default for the
570 deployment is used. If the row is present but the hard limit is
571 Null, then the resource is unlimited.
572 """
574 __tablename__ = 'quotas'
575 __table_args__ = (
576 schema.UniqueConstraint("project_id", "resource", "deleted",
577 name="uniq_quotas0project_id0resource0deleted"
578 ),
579 )
580 id = sa.Column(sa.Integer, primary_key=True)
582 project_id = sa.Column(sa.String(255))
584 resource = sa.Column(sa.String(255), nullable=False)
585 hard_limit = sa.Column(sa.Integer)
588class ProjectUserQuota(BASE, NovaBase, models.SoftDeleteMixin):
589 """Represents a single quota override for a user with in a project."""
591 __tablename__ = 'project_user_quotas'
592 uniq_name = "uniq_project_user_quotas0user_id0project_id0resource0deleted"
593 __table_args__ = (
594 schema.UniqueConstraint("user_id", "project_id", "resource", "deleted",
595 name=uniq_name),
596 sa.Index('project_user_quotas_project_id_deleted_idx',
597 'project_id', 'deleted'),
598 sa.Index('project_user_quotas_user_id_deleted_idx',
599 'user_id', 'deleted')
600 )
601 id = sa.Column(sa.Integer, primary_key=True, nullable=False)
603 project_id = sa.Column(sa.String(255), nullable=False)
604 user_id = sa.Column(sa.String(255), nullable=False)
606 resource = sa.Column(sa.String(255), nullable=False)
607 hard_limit = sa.Column(sa.Integer)
610class QuotaClass(BASE, NovaBase, models.SoftDeleteMixin):
611 """Represents a single quota override for a quota class.
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 """
618 __tablename__ = 'quota_classes'
619 __table_args__ = (
620 sa.Index('ix_quota_classes_class_name', 'class_name'),
621 )
622 id = sa.Column(sa.Integer, primary_key=True)
624 class_name = sa.Column(sa.String(255))
626 resource = sa.Column(sa.String(255))
627 hard_limit = sa.Column(sa.Integer)
630class QuotaUsage(BASE, NovaBase, models.SoftDeleteMixin):
631 """Represents the current usage for a given resource."""
633 __tablename__ = 'quota_usages'
634 __table_args__ = (
635 sa.Index('ix_quota_usages_project_id', 'project_id'),
636 sa.Index('ix_quota_usages_user_id_deleted', 'user_id', 'deleted'),
637 )
638 id = sa.Column(sa.Integer, primary_key=True)
640 project_id = sa.Column(sa.String(255))
641 user_id = sa.Column(sa.String(255))
642 resource = sa.Column(sa.String(255), nullable=False)
644 in_use = sa.Column(sa.Integer, nullable=False)
645 reserved = sa.Column(sa.Integer, nullable=False)
647 @property
648 def total(self):
649 return self.in_use + self.reserved
651 until_refresh = sa.Column(sa.Integer)
654class Reservation(BASE, NovaBase, models.SoftDeleteMixin):
655 """Represents a resource reservation for quotas."""
657 __tablename__ = 'reservations'
658 __table_args__ = (
659 sa.Index('ix_reservations_project_id', 'project_id'),
660 sa.Index('reservations_uuid_idx', 'uuid'),
661 sa.Index('reservations_deleted_expire_idx', 'deleted', 'expire'),
662 sa.Index('ix_reservations_user_id_deleted', 'user_id', 'deleted'),
663 )
664 id = sa.Column(sa.Integer, primary_key=True, nullable=False)
665 uuid = sa.Column(sa.String(36), nullable=False)
667 usage_id = sa.Column(
668 sa.Integer, sa.ForeignKey('quota_usages.id'), nullable=False)
670 project_id = sa.Column(sa.String(255))
671 user_id = sa.Column(sa.String(255))
672 resource = sa.Column(sa.String(255))
674 delta = sa.Column(sa.Integer, nullable=False)
675 expire = sa.Column(sa.DateTime)
677 usage = orm.relationship(
678 "QuotaUsage",
679 foreign_keys=usage_id,
680 primaryjoin='and_(Reservation.usage_id == QuotaUsage.id,'
681 'QuotaUsage.deleted == 0)')
684class BlockDeviceMapping(BASE, NovaBase, models.SoftDeleteMixin):
685 """Represents block device mapping that is defined by EC2."""
686 __tablename__ = "block_device_mapping"
687 __table_args__ = (
688 sa.Index('snapshot_id', 'snapshot_id'),
689 sa.Index('volume_id', 'volume_id'),
690 sa.Index('block_device_mapping_instance_uuid_device_name_idx',
691 'instance_uuid', 'device_name'),
692 sa.Index('block_device_mapping_instance_uuid_volume_id_idx',
693 'instance_uuid', 'volume_id'),
694 sa.Index('block_device_mapping_instance_uuid_idx', 'instance_uuid'),
695 schema.UniqueConstraint('uuid', name='uniq_block_device_mapping0uuid'),
696 )
697 id = sa.Column(sa.Integer, primary_key=True, autoincrement=True)
699 instance_uuid = sa.Column(sa.String(36), sa.ForeignKey('instances.uuid'))
700 # NOTE(mdbooth): The REST API for BDMs includes a UUID field. That uuid
701 # refers to an image, volume, or snapshot which will be used in the
702 # initialisation of the BDM. It is only relevant during the API call, and
703 # is not persisted directly. This is the UUID of the BDM itself.
704 # FIXME(danms): This should eventually be non-nullable, but we need a
705 # transition period first.
706 uuid = sa.Column(sa.String(36))
707 instance = orm.relationship(Instance,
708 back_populates='block_device_mapping',
709 foreign_keys=instance_uuid,
710 primaryjoin='and_(BlockDeviceMapping.'
711 'instance_uuid=='
712 'Instance.uuid,'
713 'BlockDeviceMapping.deleted=='
714 '0)')
716 source_type = sa.Column(sa.String(255))
717 destination_type = sa.Column(sa.String(255))
718 guest_format = sa.Column(sa.String(255))
719 device_type = sa.Column(sa.String(255))
720 disk_bus = sa.Column(sa.String(255))
722 boot_index = sa.Column(sa.Integer)
724 device_name = sa.Column(sa.String(255))
726 # default=False for compatibility of the existing code.
727 # With EC2 API,
728 # default True for ami specified device.
729 # default False for created with other timing.
730 # TODO(sshturm) add default in db
731 delete_on_termination = sa.Column(sa.Boolean, default=False)
733 snapshot_id = sa.Column(sa.String(36))
735 volume_id = sa.Column(sa.String(36))
736 volume_size = sa.Column(sa.Integer)
738 volume_type = sa.Column(sa.String(255))
740 image_id = sa.Column(sa.String(36))
742 # for no device to suppress devices.
743 no_device = sa.Column(sa.Boolean)
745 connection_info = sa.Column(types.MediumText())
747 tag = sa.Column(sa.String(255))
749 attachment_id = sa.Column(sa.String(36))
751 encrypted = sa.Column(sa.Boolean, default=False)
752 encryption_secret_uuid = sa.Column(sa.String(36))
753 encryption_format = sa.Column(sa.String(128))
754 encryption_options = sa.Column(sa.String(4096))
757class ShareMapping(BASE, NovaBase):
758 """Represents share / instance mapping."""
759 __tablename__ = "share_mapping"
760 __table_args__ = (
761 sa.Index('share_idx', 'share_id'),
762 sa.Index('share_mapping_instance_uuid_share_id_idx',
763 'instance_uuid', 'share_id'),
764 sa.UniqueConstraint(
765 "instance_uuid",
766 "share_id",
767 name="uniq_key_pairs0instance_uuid0share_id",
768 ),
769 sa.UniqueConstraint(
770 "instance_uuid",
771 "tag",
772 name="uniq_key_pairs0instance_uuid0tag",
773 ),
774 )
775 # sqlite> create table my_table(id bigint primary key AUTOINCREMENT,
776 # name text);
777 # Parse error: AUTOINCREMENT is only allowed on an INTEGER PRIMARY KEY
778 # Use BigInteger variant for sqlite to allow unit tests. Other database
779 # should support BigInteger and autoincrement.
780 id = sa.Column(
781 sa.BigInteger().with_variant(sa.Integer, "sqlite"),
782 primary_key=True,
783 autoincrement=True,
784 nullable=False,
785 )
786 uuid = sa.Column(sa.String(36))
787 instance_uuid = sa.Column(sa.String(36), sa.ForeignKey('instances.uuid'))
788 instance = orm.relationship(
789 "Instance",
790 foreign_keys=instance_uuid,
791 primaryjoin='and_(ShareMapping.instance_uuid == Instance.uuid,'
792 'Instance.deleted == 0)'
793 )
794 share_id = sa.Column(sa.String(36))
795 status = sa.Column(sa.String(32))
796 tag = sa.Column(sa.String(48))
797 export_location = sa.Column(sa.Text)
798 share_proto = sa.Column(sa.String(32))
801# TODO(stephenfin): Remove once we drop the security_groups field from the
802# Instance table. Until then, this is tied to the SecurityGroup table
803class SecurityGroupInstanceAssociation(BASE, NovaBase, models.SoftDeleteMixin):
804 __tablename__ = 'security_group_instance_association'
805 __table_args__ = (
806 sa.Index('security_group_instance_association_instance_uuid_idx',
807 'instance_uuid'),
808 )
809 id = sa.Column(sa.Integer, primary_key=True, nullable=False)
810 security_group_id = sa.Column(
811 sa.Integer, sa.ForeignKey('security_groups.id'))
812 instance_uuid = sa.Column(sa.String(36), sa.ForeignKey('instances.uuid'))
815# TODO(stephenfin): Remove once we drop the security_groups field from the
816# Instance table
817class SecurityGroup(BASE, NovaBase, models.SoftDeleteMixin):
818 """Represents a security group."""
819 __tablename__ = 'security_groups'
820 __table_args__ = (
821 schema.UniqueConstraint('project_id', 'name', 'deleted',
822 name='uniq_security_groups0project_id0'
823 'name0deleted'),
824 )
825 id = sa.Column(sa.Integer, primary_key = True)
827 name = sa.Column(sa.String(255))
828 description = sa.Column(sa.String(255))
829 user_id = sa.Column(sa.String(255))
830 project_id = sa.Column(sa.String(255))
832 instances = orm.relationship(Instance,
833 back_populates='security_groups',
834 secondary = "security_group_instance_association",
835 primaryjoin = 'and_('
836 'SecurityGroup.id == '
837 'SecurityGroupInstanceAssociation.security_group_id,'
838 'SecurityGroupInstanceAssociation.deleted == 0,'
839 'SecurityGroup.deleted == 0)',
840 secondaryjoin='and_('
841 'SecurityGroupInstanceAssociation.instance_uuid == Instance.uuid,'
842 # (anthony) the condition below shouldn't be necessary now that the
843 # association is being marked as deleted. However, removing this
844 # may cause existing deployments to choke, so I'm leaving it
845 'Instance.deleted == 0)')
847 rules = orm.relationship(
848 'SecurityGroupIngressRule',
849 primaryjoin=(
850 'and_('
851 'SecurityGroupIngressRule.parent_group_id == SecurityGroup.id,'
852 'SecurityGroupIngressRule.deleted == 0)'
853 ),
854 back_populates='parent_group',
855 )
858# TODO(stephenfin): Remove once we drop the security_groups field from the
859# Instance table. Until then, this is tied to the SecurityGroup table
860class SecurityGroupIngressRule(BASE, NovaBase, models.SoftDeleteMixin):
861 """Represents a rule in a security group."""
862 __tablename__ = 'security_group_rules'
863 __table_args__ = ()
864 id = sa.Column(sa.Integer, primary_key=True)
866 parent_group_id = sa.Column(
867 sa.Integer, sa.ForeignKey('security_groups.id'))
868 parent_group = orm.relationship("SecurityGroup",
869 back_populates="rules",
870 foreign_keys=[parent_group_id],
871 primaryjoin='and_('
872 'SecurityGroupIngressRule.parent_group_id == SecurityGroup.id,'
873 'SecurityGroupIngressRule.deleted == 0)')
875 protocol = sa.Column(sa.String(255))
876 from_port = sa.Column(sa.Integer)
877 to_port = sa.Column(sa.Integer)
878 cidr = sa.Column(types.CIDR())
880 # Note: This is not the parent SecurityGroup. It's SecurityGroup we're
881 # granting access for.
882 group_id = sa.Column(sa.Integer, sa.ForeignKey('security_groups.id'))
883 grantee_group = orm.relationship("SecurityGroup",
884 foreign_keys=[group_id],
885 primaryjoin='and_('
886 'SecurityGroupIngressRule.group_id == SecurityGroup.id,'
887 'SecurityGroupIngressRule.deleted == 0)')
890class Migration(BASE, NovaBase, models.SoftDeleteMixin):
891 """Represents a running host-to-host migration."""
892 __tablename__ = 'migrations'
893 __table_args__ = (
894 sa.Index('migrations_instance_uuid_and_status_idx', 'deleted',
895 'instance_uuid', 'status'),
896 sa.Index('migrations_by_host_nodes_and_status_idx', 'deleted',
897 'source_compute', 'dest_compute', 'source_node', 'dest_node',
898 'status'),
899 sa.Index('migrations_uuid', 'uuid', unique=True),
900 sa.Index('migrations_updated_at_idx', 'updated_at'),
901 sa.Index('migrations_dest_compute_id_deleted_idx',
902 'dest_compute_id', 'deleted'),
903 )
904 id = sa.Column(sa.Integer, primary_key=True, nullable=False)
905 # NOTE(tr3buchet): the ____compute variables are instance['host']
906 source_compute = sa.Column(sa.String(255))
907 dest_compute = sa.Column(sa.String(255))
908 # nodes are equivalent to a compute node's 'hypervisor_hostname'
909 source_node = sa.Column(sa.String(255))
910 dest_node = sa.Column(sa.String(255))
911 # The ID of the ComputeNode that matches dest_node
912 dest_compute_id = sa.Column(sa.BigInteger())
913 # NOTE(tr3buchet): dest_host, btw, is an ip address
914 dest_host = sa.Column(sa.String(255))
915 old_instance_type_id = sa.Column(sa.Integer())
916 new_instance_type_id = sa.Column(sa.Integer())
917 instance_uuid = sa.Column(sa.String(36), sa.ForeignKey('instances.uuid'))
918 uuid = sa.Column(sa.String(36), nullable=True)
919 # TODO(_cerberus_): enum
920 status = sa.Column(sa.String(255))
921 migration_type = sa.Column(sa.Enum('migration', 'resize', 'live-migration',
922 'evacuation', name='migration_type'),
923 nullable=True)
924 hidden = sa.Column(sa.Boolean, default=False)
925 memory_total = sa.Column(sa.BigInteger, nullable=True)
926 memory_processed = sa.Column(sa.BigInteger, nullable=True)
927 memory_remaining = sa.Column(sa.BigInteger, nullable=True)
928 disk_total = sa.Column(sa.BigInteger, nullable=True)
929 disk_processed = sa.Column(sa.BigInteger, nullable=True)
930 disk_remaining = sa.Column(sa.BigInteger, nullable=True)
931 cross_cell_move = sa.Column(sa.Boolean, default=False)
933 user_id = sa.Column(sa.String(255), nullable=True)
934 project_id = sa.Column(sa.String(255), nullable=True)
936 instance = orm.relationship("Instance", foreign_keys=instance_uuid,
937 primaryjoin='and_(Migration.instance_uuid == '
938 'Instance.uuid, Instance.deleted == '
939 '0)')
942class VirtualInterface(BASE, NovaBase, models.SoftDeleteMixin):
943 """Represents a virtual interface on an instance."""
944 __tablename__ = 'virtual_interfaces'
945 __table_args__ = (
946 schema.UniqueConstraint("address", "deleted",
947 name="uniq_virtual_interfaces0address0deleted"),
948 sa.Index('virtual_interfaces_network_id_idx', 'network_id'),
949 sa.Index('virtual_interfaces_instance_uuid_fkey', 'instance_uuid'),
950 sa.Index('virtual_interfaces_uuid_idx', 'uuid'),
951 )
952 id = sa.Column(sa.Integer, primary_key=True, nullable=False)
953 address = sa.Column(sa.String(255))
954 network_id = sa.Column(sa.Integer)
955 instance_uuid = sa.Column(sa.String(36), sa.ForeignKey('instances.uuid'))
956 uuid = sa.Column(sa.String(36))
957 tag = sa.Column(sa.String(255))
960class InstanceMetadata(BASE, NovaBase, models.SoftDeleteMixin):
961 """Represents a user-provided metadata key/value pair for an instance."""
962 __tablename__ = 'instance_metadata'
963 __table_args__ = (
964 sa.Index('instance_metadata_instance_uuid_idx', 'instance_uuid'),
965 )
966 id = sa.Column(sa.Integer, primary_key=True)
967 key = sa.Column(sa.String(255))
968 value = sa.Column(sa.String(255))
969 instance_uuid = sa.Column(sa.String(36), sa.ForeignKey('instances.uuid'))
971 instance = orm.relationship(
972 Instance,
973 back_populates="metadata",
974 foreign_keys=instance_uuid,
975 primaryjoin=(
976 'and_(InstanceMetadata.instance_uuid == Instance.uuid,'
977 'InstanceMetadata.deleted == 0)'
978 ),
979 )
982class InstanceSystemMetadata(BASE, NovaBase, models.SoftDeleteMixin):
983 """Represents a system-owned metadata key/value pair for an instance."""
984 __tablename__ = 'instance_system_metadata'
985 __table_args__ = (
986 sa.Index('instance_uuid', 'instance_uuid'),
987 )
988 id = sa.Column(sa.Integer, primary_key=True)
989 key = sa.Column(sa.String(255), nullable=False)
990 value = sa.Column(sa.String(255))
991 instance_uuid = sa.Column(sa.String(36),
992 sa.ForeignKey('instances.uuid'),
993 nullable=False)
995 instance = orm.relationship(
996 Instance,
997 back_populates='system_metadata',
998 foreign_keys=instance_uuid,
999 )
1002class VolumeUsage(BASE, NovaBase, models.SoftDeleteMixin):
1003 """Cache for volume usage data pulled from the hypervisor."""
1004 __tablename__ = 'volume_usage_cache'
1005 __table_args__ = ()
1006 id = sa.Column(sa.Integer, primary_key=True, nullable=False)
1007 volume_id = sa.Column(sa.String(36), nullable=False)
1008 instance_uuid = sa.Column(sa.String(36))
1009 project_id = sa.Column(sa.String(36))
1010 user_id = sa.Column(sa.String(64))
1011 availability_zone = sa.Column(sa.String(255))
1012 tot_last_refreshed = sa.Column(sa.DateTime)
1013 tot_reads = sa.Column(sa.BigInteger, default=0)
1014 tot_read_bytes = sa.Column(sa.BigInteger, default=0)
1015 tot_writes = sa.Column(sa.BigInteger, default=0)
1016 tot_write_bytes = sa.Column(sa.BigInteger, default=0)
1017 curr_last_refreshed = sa.Column(sa.DateTime)
1018 curr_reads = sa.Column(sa.BigInteger, default=0)
1019 curr_read_bytes = sa.Column(sa.BigInteger, default=0)
1020 curr_writes = sa.Column(sa.BigInteger, default=0)
1021 curr_write_bytes = sa.Column(sa.BigInteger, default=0)
1024class S3Image(BASE, NovaBase, models.SoftDeleteMixin):
1025 """Compatibility layer for the S3 image service talking to Glance."""
1026 __tablename__ = 's3_images'
1027 __table_args__ = ()
1028 id = sa.Column(
1029 sa.Integer, primary_key=True, nullable=False, autoincrement=True)
1030 uuid = sa.Column(sa.String(36), nullable=False)
1033class InstanceFault(BASE, NovaBase, models.SoftDeleteMixin):
1034 __tablename__ = 'instance_faults'
1035 __table_args__ = (
1036 sa.Index('instance_faults_host_idx', 'host'),
1037 sa.Index('instance_faults_instance_uuid_deleted_created_at_idx',
1038 'instance_uuid', 'deleted', 'created_at')
1039 )
1041 id = sa.Column(sa.Integer, primary_key=True, nullable=False)
1042 instance_uuid = sa.Column(sa.String(36),
1043 sa.ForeignKey('instances.uuid'))
1044 code = sa.Column(sa.Integer(), nullable=False)
1045 message = sa.Column(sa.String(255))
1046 details = sa.Column(types.MediumText())
1047 host = sa.Column(sa.String(255))
1050class InstanceAction(BASE, NovaBase, models.SoftDeleteMixin):
1051 """Track client actions on an instance.
1053 The intention is that there will only be one of these per user request. A
1054 lookup by (instance_uuid, request_id) should always return a single result.
1055 """
1056 __tablename__ = 'instance_actions'
1057 __table_args__ = (
1058 sa.Index('instance_uuid_idx', 'instance_uuid'),
1059 sa.Index('request_id_idx', 'request_id'),
1060 sa.Index('instance_actions_instance_uuid_updated_at_idx',
1061 'instance_uuid', 'updated_at')
1062 )
1064 id = sa.Column(
1065 sa.Integer, primary_key=True, nullable=False, autoincrement=True)
1066 action = sa.Column(sa.String(255))
1067 instance_uuid = sa.Column(sa.String(36),
1068 sa.ForeignKey('instances.uuid'))
1069 request_id = sa.Column(sa.String(255))
1070 user_id = sa.Column(sa.String(255))
1071 project_id = sa.Column(sa.String(255))
1072 start_time = sa.Column(sa.DateTime, default=timeutils.utcnow)
1073 finish_time = sa.Column(sa.DateTime)
1074 message = sa.Column(sa.String(255))
1077class InstanceActionEvent(BASE, NovaBase, models.SoftDeleteMixin):
1078 """Track events that occur during an InstanceAction."""
1079 __tablename__ = 'instance_actions_events'
1080 __table_args__ = ()
1082 id = sa.Column(
1083 sa.Integer, primary_key=True, nullable=False, autoincrement=True)
1084 event = sa.Column(sa.String(255))
1085 action_id = sa.Column(sa.Integer, sa.ForeignKey('instance_actions.id'))
1086 start_time = sa.Column(sa.DateTime, default=timeutils.utcnow)
1087 finish_time = sa.Column(sa.DateTime)
1088 result = sa.Column(sa.String(255))
1089 traceback = sa.Column(sa.Text)
1090 host = sa.Column(sa.String(255))
1091 details = sa.Column(sa.Text)
1094class InstanceIdMapping(BASE, NovaBase, models.SoftDeleteMixin):
1095 """Compatibility layer for the EC2 instance service."""
1096 __tablename__ = 'instance_id_mappings'
1097 __table_args__ = (
1098 sa.Index('ix_instance_id_mappings_uuid', 'uuid'),
1099 )
1100 id = sa.Column(
1101 sa.Integer, primary_key=True, nullable=False, autoincrement=True)
1102 uuid = sa.Column(sa.String(36), nullable=False)
1105class TaskLog(BASE, NovaBase, models.SoftDeleteMixin):
1106 """Audit log for background periodic tasks."""
1107 __tablename__ = 'task_log'
1108 __table_args__ = (
1109 schema.UniqueConstraint(
1110 'task_name', 'host', 'period_beginning', 'period_ending',
1111 name="uniq_task_log0task_name0host0period_beginning0period_ending"
1112 ),
1113 sa.Index('ix_task_log_period_beginning', 'period_beginning'),
1114 sa.Index('ix_task_log_host', 'host'),
1115 sa.Index('ix_task_log_period_ending', 'period_ending'),
1116 )
1117 id = sa.Column(
1118 sa.Integer, primary_key=True, nullable=False, autoincrement=True)
1119 task_name = sa.Column(sa.String(255), nullable=False)
1120 state = sa.Column(sa.String(255), nullable=False)
1121 host = sa.Column(sa.String(255), nullable=False)
1122 period_beginning = sa.Column(sa.DateTime, default=timeutils.utcnow,
1123 nullable=False)
1124 period_ending = sa.Column(sa.DateTime, default=timeutils.utcnow,
1125 nullable=False)
1126 message = sa.Column(sa.String(255), nullable=False)
1127 task_items = sa.Column(sa.Integer(), default=0)
1128 errors = sa.Column(sa.Integer(), default=0)
1131class PciDevice(BASE, NovaBase, models.SoftDeleteMixin):
1132 """Represents a PCI host device that can be passed through to instances.
1133 """
1134 __tablename__ = 'pci_devices'
1135 __table_args__ = (
1136 sa.Index('ix_pci_devices_compute_node_id_deleted',
1137 'compute_node_id', 'deleted'),
1138 sa.Index('ix_pci_devices_instance_uuid_deleted',
1139 'instance_uuid', 'deleted'),
1140 sa.Index('ix_pci_devices_compute_node_id_parent_addr_deleted',
1141 'compute_node_id', 'parent_addr', 'deleted'),
1142 schema.UniqueConstraint(
1143 "compute_node_id", "address", "deleted",
1144 name="uniq_pci_devices0compute_node_id0address0deleted")
1145 )
1146 id = sa.Column(sa.Integer, primary_key=True)
1147 uuid = sa.Column(sa.String(36))
1148 compute_node_id = sa.Column(sa.Integer, sa.ForeignKey('compute_nodes.id'),
1149 nullable=False)
1151 # physical address of device domain:bus:slot.func (0000:09:01.1)
1152 address = sa.Column(sa.String(12), nullable=False)
1154 vendor_id = sa.Column(sa.String(4), nullable=False)
1155 product_id = sa.Column(sa.String(4), nullable=False)
1156 dev_type = sa.Column(sa.String(8), nullable=False)
1157 dev_id = sa.Column(sa.String(255))
1159 # label is abstract device name, that is used to unify devices with the
1160 # same functionality with different addresses or host.
1161 label = sa.Column(sa.String(255), nullable=False)
1163 status = sa.Column(sa.String(36), nullable=False)
1164 # the request_id is used to identify a device that is allocated for a
1165 # particular request
1166 request_id = sa.Column(sa.String(36), nullable=True)
1168 extra_info = sa.Column(sa.Text)
1170 instance_uuid = sa.Column(sa.String(36))
1172 numa_node = sa.Column(sa.Integer, nullable=True)
1174 parent_addr = sa.Column(sa.String(12), nullable=True)
1175 instance = orm.relationship(Instance,
1176 back_populates="pci_devices",
1177 foreign_keys=instance_uuid,
1178 primaryjoin='and_('
1179 'PciDevice.instance_uuid == Instance.uuid,'
1180 'PciDevice.deleted == 0)')
1183class Tag(BASE, models.ModelBase):
1184 """Represents the tag for a resource."""
1186 __tablename__ = "tags"
1187 __table_args__ = (
1188 sa.Index('tags_tag_idx', 'tag'),
1189 )
1190 resource_id = sa.Column(sa.String(36), primary_key=True, nullable=False)
1191 tag = sa.Column(sa.Unicode(80), primary_key=True, nullable=False)
1193 instance = orm.relationship(
1194 'Instance',
1195 back_populates='tags',
1196 foreign_keys=resource_id,
1197 primaryjoin='and_(Tag.resource_id == Instance.uuid,'
1198 'Instance.deleted == 0)',
1199 )
1202class ConsoleAuthToken(BASE, NovaBase):
1203 """Represents a console auth token"""
1205 __tablename__ = 'console_auth_tokens'
1206 __table_args__ = (
1207 sa.Index('console_auth_tokens_instance_uuid_idx', 'instance_uuid'),
1208 sa.Index('console_auth_tokens_host_expires_idx', 'host', 'expires'),
1209 sa.Index(
1210 'console_auth_tokens_token_hash_instance_uuid_idx', 'token_hash',
1211 'instance_uuid',
1212 ),
1213 schema.UniqueConstraint("token_hash",
1214 name="uniq_console_auth_tokens0token_hash")
1215 )
1216 id = sa.Column(sa.Integer, primary_key=True, nullable=False)
1217 token_hash = sa.Column(sa.String(255), nullable=False)
1218 console_type = sa.Column(sa.String(255), nullable=False)
1219 host = sa.Column(sa.String(255), nullable=False)
1220 port = sa.Column(sa.Integer, nullable=False)
1221 tls_port = sa.Column(sa.Integer, nullable=True)
1222 internal_access_path = sa.Column(sa.String(255))
1223 instance_uuid = sa.Column(sa.String(36), nullable=False)
1224 expires = sa.Column(sa.Integer, nullable=False)
1225 access_url_base = sa.Column(sa.String(255))
1227 instance = orm.relationship(
1228 "Instance",
1229 back_populates='console_auth_tokens',
1230 primaryjoin='and_(ConsoleAuthToken.instance_uuid == Instance.uuid,'
1231 'Instance.deleted == 0)',
1232 foreign_keys=instance_uuid
1233 )