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

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""" 

21 

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 

30 

31from nova.db import types 

32 

33CONF = cfg.CONF 

34 

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} 

48 

49# NOTE(stephenfin): A list of foreign key constraints that were removed when 

50# the column they were covering was removed. 

51REMOVED_FKEYS = [] 

52 

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', 

71 

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', 

78 

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', 

82 

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', 

86 

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', 

91 

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} 

101 

102# we don't configure 'cls' since we have models that don't use the 

103# TimestampMixin 

104BASE = declarative.declarative_base() 

105 

106 

107class NovaBase(models.TimestampMixin, models.ModelBase): 

108 

109 def __copy__(self): 

110 """Implement a safe copy.copy(). 

111 

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. 

118 

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. 

123 

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. 

130 

131 """ 

132 session = orm.Session() 

133 

134 copy = session.merge(self, load=False) 

135 session.expunge(copy) 

136 return copy 

137 

138 

139class Service(BASE, NovaBase, models.SoftDeleteMixin): 

140 """Represents a running service on a host.""" 

141 

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 ) 

150 

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) 

162 

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 ) 

171 

172 

173class ComputeNode(BASE, NovaBase, models.SoftDeleteMixin): 

174 """Represents a running compute service on a host.""" 

175 

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) 

185 

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)) 

202 

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) 

210 

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) 

226 

227 # Note(yongli): json string PCI Stats 

228 # '[{"vendor_id":"8086", "product_id":"1234", "count":3 }, ...]' 

229 pci_stats = sa.Column(sa.Text) 

230 

231 # extra_resources is a json string containing arbitrary 

232 # data about additional resources. 

233 extra_resources = sa.Column(sa.Text) 

234 

235 # json-encode string containing compute node statistics 

236 stats = sa.Column(sa.Text, default='{}') 

237 

238 # json-encoded dict that contains NUMA topology as generated by 

239 # objects.NUMATopology._to_json() 

240 numa_topology = sa.Column(sa.Text) 

241 

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) 

247 

248 

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) 

259 

260 user_id = sa.Column(sa.String(255)) 

261 project_id = sa.Column(sa.String(255)) 

262 file_name = sa.Column(sa.String(255)) 

263 

264 

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 = [] 

293 

294 id = sa.Column(sa.Integer, primary_key=True, autoincrement=True) 

295 

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 

317 

318 @property 

319 def _extra_keys(self): 

320 return ['name'] 

321 

322 user_id = sa.Column(sa.String(255)) 

323 project_id = sa.Column(sa.String(255)) 

324 

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)) 

329 

330 launch_index = sa.Column(sa.Integer) 

331 key_name = sa.Column(sa.String(255)) 

332 key_data = sa.Column(types.MediumText()) 

333 

334 power_state = sa.Column(sa.Integer) 

335 vm_state = sa.Column(sa.String(255)) 

336 task_state = sa.Column(sa.String(255)) 

337 

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)) 

343 

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()) 

354 

355 # *not* flavorid, this is the internal primary_key 

356 instance_type_id = sa.Column(sa.Integer) 

357 

358 user_data = sa.Column(types.MediumText()) 

359 

360 reservation_id = sa.Column(sa.String(255)) 

361 

362 launched_at = sa.Column(sa.DateTime) 

363 terminated_at = sa.Column(sa.DateTime) 

364 

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)) 

369 

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)) 

373 

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()) 

377 

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')) 

383 

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) 

388 

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)) 

393 

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()) 

398 

399 auto_disk_config = sa.Column(sa.Boolean()) 

400 progress = sa.Column(sa.Integer) 

401 

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) 

408 

409 # EC2 disable_api_termination 

410 disable_terminate = sa.Column(sa.Boolean(), default=False) 

411 

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)) 

416 

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) 

422 

423 # Records whether an instance has been deleted from disk 

424 cleaned = sa.Column(sa.Integer, default=0) 

425 

426 hidden = sa.Column(sa.Boolean, default=False) 

427 

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 ) 

505 

506 

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) 

517 

518 

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) 

528 

529 # text column used for storing a json object of network data for api 

530 network_info = sa.Column(types.MediumText()) 

531 

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) 

538 

539 

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) 

562 

563 

564class Quota(BASE, NovaBase, models.SoftDeleteMixin): 

565 """Represents a single quota override for a project. 

566 

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 """ 

573 

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) 

581 

582 project_id = sa.Column(sa.String(255)) 

583 

584 resource = sa.Column(sa.String(255), nullable=False) 

585 hard_limit = sa.Column(sa.Integer) 

586 

587 

588class ProjectUserQuota(BASE, NovaBase, models.SoftDeleteMixin): 

589 """Represents a single quota override for a user with in a project.""" 

590 

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) 

602 

603 project_id = sa.Column(sa.String(255), nullable=False) 

604 user_id = sa.Column(sa.String(255), nullable=False) 

605 

606 resource = sa.Column(sa.String(255), nullable=False) 

607 hard_limit = sa.Column(sa.Integer) 

608 

609 

610class QuotaClass(BASE, NovaBase, models.SoftDeleteMixin): 

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 __table_args__ = ( 

620 sa.Index('ix_quota_classes_class_name', 'class_name'), 

621 ) 

622 id = sa.Column(sa.Integer, primary_key=True) 

623 

624 class_name = sa.Column(sa.String(255)) 

625 

626 resource = sa.Column(sa.String(255)) 

627 hard_limit = sa.Column(sa.Integer) 

628 

629 

630class QuotaUsage(BASE, NovaBase, models.SoftDeleteMixin): 

631 """Represents the current usage for a given resource.""" 

632 

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) 

639 

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) 

643 

644 in_use = sa.Column(sa.Integer, nullable=False) 

645 reserved = sa.Column(sa.Integer, nullable=False) 

646 

647 @property 

648 def total(self): 

649 return self.in_use + self.reserved 

650 

651 until_refresh = sa.Column(sa.Integer) 

652 

653 

654class Reservation(BASE, NovaBase, models.SoftDeleteMixin): 

655 """Represents a resource reservation for quotas.""" 

656 

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) 

666 

667 usage_id = sa.Column( 

668 sa.Integer, sa.ForeignKey('quota_usages.id'), nullable=False) 

669 

670 project_id = sa.Column(sa.String(255)) 

671 user_id = sa.Column(sa.String(255)) 

672 resource = sa.Column(sa.String(255)) 

673 

674 delta = sa.Column(sa.Integer, nullable=False) 

675 expire = sa.Column(sa.DateTime) 

676 

677 usage = orm.relationship( 

678 "QuotaUsage", 

679 foreign_keys=usage_id, 

680 primaryjoin='and_(Reservation.usage_id == QuotaUsage.id,' 

681 'QuotaUsage.deleted == 0)') 

682 

683 

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) 

698 

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)') 

715 

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)) 

721 

722 boot_index = sa.Column(sa.Integer) 

723 

724 device_name = sa.Column(sa.String(255)) 

725 

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) 

732 

733 snapshot_id = sa.Column(sa.String(36)) 

734 

735 volume_id = sa.Column(sa.String(36)) 

736 volume_size = sa.Column(sa.Integer) 

737 

738 volume_type = sa.Column(sa.String(255)) 

739 

740 image_id = sa.Column(sa.String(36)) 

741 

742 # for no device to suppress devices. 

743 no_device = sa.Column(sa.Boolean) 

744 

745 connection_info = sa.Column(types.MediumText()) 

746 

747 tag = sa.Column(sa.String(255)) 

748 

749 attachment_id = sa.Column(sa.String(36)) 

750 

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)) 

755 

756 

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)) 

799 

800 

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')) 

813 

814 

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) 

826 

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)) 

831 

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)') 

846 

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 ) 

856 

857 

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) 

865 

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)') 

874 

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()) 

879 

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)') 

888 

889 

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) 

932 

933 user_id = sa.Column(sa.String(255), nullable=True) 

934 project_id = sa.Column(sa.String(255), nullable=True) 

935 

936 instance = orm.relationship("Instance", foreign_keys=instance_uuid, 

937 primaryjoin='and_(Migration.instance_uuid == ' 

938 'Instance.uuid, Instance.deleted == ' 

939 '0)') 

940 

941 

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)) 

958 

959 

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')) 

970 

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 ) 

980 

981 

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) 

994 

995 instance = orm.relationship( 

996 Instance, 

997 back_populates='system_metadata', 

998 foreign_keys=instance_uuid, 

999 ) 

1000 

1001 

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) 

1022 

1023 

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) 

1031 

1032 

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 ) 

1040 

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)) 

1048 

1049 

1050class InstanceAction(BASE, NovaBase, models.SoftDeleteMixin): 

1051 """Track client actions on an instance. 

1052 

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 ) 

1063 

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)) 

1075 

1076 

1077class InstanceActionEvent(BASE, NovaBase, models.SoftDeleteMixin): 

1078 """Track events that occur during an InstanceAction.""" 

1079 __tablename__ = 'instance_actions_events' 

1080 __table_args__ = () 

1081 

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) 

1092 

1093 

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) 

1103 

1104 

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) 

1129 

1130 

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) 

1150 

1151 # physical address of device domain:bus:slot.func (0000:09:01.1) 

1152 address = sa.Column(sa.String(12), nullable=False) 

1153 

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)) 

1158 

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) 

1162 

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) 

1167 

1168 extra_info = sa.Column(sa.Text) 

1169 

1170 instance_uuid = sa.Column(sa.String(36)) 

1171 

1172 numa_node = sa.Column(sa.Integer, nullable=True) 

1173 

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)') 

1181 

1182 

1183class Tag(BASE, models.ModelBase): 

1184 """Represents the tag for a resource.""" 

1185 

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) 

1192 

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 ) 

1200 

1201 

1202class ConsoleAuthToken(BASE, NovaBase): 

1203 """Represents a console auth token""" 

1204 

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)) 

1226 

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 )