summaryrefslogtreecommitdiffstats
path: root/nova/db
diff options
context:
space:
mode:
authorIsaku Yamahata <yamahata@valinux.co.jp>2011-07-08 12:07:58 +0900
committerIsaku Yamahata <yamahata@valinux.co.jp>2011-07-08 12:07:58 +0900
commita02895b6bb353a468ce7c58e60bc2dbd152c5ec9 (patch)
tree605c2efa569a42fd6f059299da1316edb597fec1 /nova/db
parent02c0bf3b242395e63baf582b1f9c279eef4282d6 (diff)
parentbc8f009f8ac6393301dd857339918d40b93be63d (diff)
downloadnova-a02895b6bb353a468ce7c58e60bc2dbd152c5ec9.tar.gz
nova-a02895b6bb353a468ce7c58e60bc2dbd152c5ec9.tar.xz
nova-a02895b6bb353a468ce7c58e60bc2dbd152c5ec9.zip
merge with trunk
Diffstat (limited to 'nova/db')
-rw-r--r--nova/db/api.py159
-rw-r--r--nova/db/sqlalchemy/api.py795
-rw-r--r--nova/db/sqlalchemy/migrate_repo/versions/027_add_provider_firewall_rules.py3
-rw-r--r--nova/db/sqlalchemy/migrate_repo/versions/028_add_instance_type_extra_specs.py67
-rw-r--r--nova/db/sqlalchemy/migrate_repo/versions/029_add_zone_weight_offsets.py38
-rw-r--r--nova/db/sqlalchemy/migrate_repo/versions/030_multi_nic.py125
-rw-r--r--nova/db/sqlalchemy/migrate_repo/versions/031_fk_fixed_ips_virtual_interface_id.py56
-rw-r--r--nova/db/sqlalchemy/migrate_repo/versions/031_sqlite_downgrade.sql48
-rw-r--r--nova/db/sqlalchemy/migrate_repo/versions/031_sqlite_upgrade.sql48
-rw-r--r--nova/db/sqlalchemy/models.py109
10 files changed, 1171 insertions, 277 deletions
diff --git a/nova/db/api.py b/nova/db/api.py
index b2f1ce688..cb4da169c 100644
--- a/nova/db/api.py
+++ b/nova/db/api.py
@@ -55,11 +55,6 @@ IMPL = utils.LazyPluggable(FLAGS['db_backend'],
sqlalchemy='nova.db.sqlalchemy.api')
-class NoMoreAddresses(exception.Error):
- """No more available addresses."""
- pass
-
-
class NoMoreBlades(exception.Error):
"""No more available blades."""
pass
@@ -223,14 +218,17 @@ def certificate_update(context, certificate_id, values):
###################
+def floating_ip_get(context, id):
+ return IMPL.floating_ip_get(context, id)
-def floating_ip_allocate_address(context, host, project_id):
+
+def floating_ip_allocate_address(context, project_id):
"""Allocate free floating ip and return the address.
Raises if one is not available.
"""
- return IMPL.floating_ip_allocate_address(context, host, project_id)
+ return IMPL.floating_ip_allocate_address(context, project_id)
def floating_ip_create(context, values):
@@ -321,6 +319,7 @@ def migration_get_by_instance_and_status(context, instance_id, status):
return IMPL.migration_get_by_instance_and_status(context, instance_id,
status)
+
####################
@@ -372,9 +371,14 @@ def fixed_ip_get_by_address(context, address):
return IMPL.fixed_ip_get_by_address(context, address)
-def fixed_ip_get_all_by_instance(context, instance_id):
+def fixed_ip_get_by_instance(context, instance_id):
"""Get fixed ips by instance or raise if none exist."""
- return IMPL.fixed_ip_get_all_by_instance(context, instance_id)
+ return IMPL.fixed_ip_get_by_instance(context, instance_id)
+
+
+def fixed_ip_get_by_virtual_interface(context, vif_id):
+ """Get fixed ips by virtual interface or raise if none exist."""
+ return IMPL.fixed_ip_get_by_virtual_interface(context, vif_id)
def fixed_ip_get_instance(context, address):
@@ -399,6 +403,62 @@ def fixed_ip_update(context, address, values):
####################
+def virtual_interface_create(context, values):
+ """Create a virtual interface record in the database."""
+ return IMPL.virtual_interface_create(context, values)
+
+
+def virtual_interface_update(context, vif_id, values):
+ """Update a virtual interface record in the database."""
+ return IMPL.virtual_interface_update(context, vif_id, values)
+
+
+def virtual_interface_get(context, vif_id):
+ """Gets a virtual interface from the table,"""
+ return IMPL.virtual_interface_get(context, vif_id)
+
+
+def virtual_interface_get_by_address(context, address):
+ """Gets a virtual interface from the table filtering on address."""
+ return IMPL.virtual_interface_get_by_address(context, address)
+
+
+def virtual_interface_get_by_fixed_ip(context, fixed_ip_id):
+ """Gets the virtual interface fixed_ip is associated with."""
+ return IMPL.virtual_interface_get_by_fixed_ip(context, fixed_ip_id)
+
+
+def virtual_interface_get_by_instance(context, instance_id):
+ """Gets all virtual_interfaces for instance."""
+ return IMPL.virtual_interface_get_by_instance(context, instance_id)
+
+
+def virtual_interface_get_by_instance_and_network(context, instance_id,
+ network_id):
+ """Gets all virtual interfaces for instance."""
+ return IMPL.virtual_interface_get_by_instance_and_network(context,
+ instance_id,
+ network_id)
+
+
+def virtual_interface_get_by_network(context, network_id):
+ """Gets all virtual interfaces on network."""
+ return IMPL.virtual_interface_get_by_network(context, network_id)
+
+
+def virtual_interface_delete(context, vif_id):
+ """Delete virtual interface record from the database."""
+ return IMPL.virtual_interface_delete(context, vif_id)
+
+
+def virtual_interface_delete_by_instance(context, instance_id):
+ """Delete virtual interface records associated with instance."""
+ return IMPL.virtual_interface_delete_by_instance(context, instance_id)
+
+
+####################
+
+
def instance_create(context, values):
"""Create an instance from the values dictionary."""
return IMPL.instance_create(context, values)
@@ -434,6 +494,11 @@ def instance_get_all(context):
return IMPL.instance_get_all(context)
+def instance_get_active_by_window(context, begin, end=None):
+ """Get instances active during a certain time window."""
+ return IMPL.instance_get_active_by_window(context, begin, end)
+
+
def instance_get_all_by_user(context, user_id):
"""Get all instances."""
return IMPL.instance_get_all_by_user(context, user_id)
@@ -454,13 +519,13 @@ def instance_get_all_by_reservation(context, reservation_id):
return IMPL.instance_get_all_by_reservation(context, reservation_id)
-def instance_get_fixed_address(context, instance_id):
+def instance_get_fixed_addresses(context, instance_id):
"""Get the fixed ip address of an instance."""
- return IMPL.instance_get_fixed_address(context, instance_id)
+ return IMPL.instance_get_fixed_addresses(context, instance_id)
-def instance_get_fixed_address_v6(context, instance_id):
- return IMPL.instance_get_fixed_address_v6(context, instance_id)
+def instance_get_fixed_addresses_v6(context, instance_id):
+ return IMPL.instance_get_fixed_addresses_v6(context, instance_id)
def instance_get_floating_address(context, instance_id):
@@ -555,9 +620,9 @@ def key_pair_get_all_by_user(context, user_id):
####################
-def network_associate(context, project_id):
+def network_associate(context, project_id, force=False):
"""Associate a free network to a project."""
- return IMPL.network_associate(context, project_id)
+ return IMPL.network_associate(context, project_id, force)
def network_count(context):
@@ -650,6 +715,11 @@ def network_get_all_by_instance(context, instance_id):
return IMPL.network_get_all_by_instance(context, instance_id)
+def network_get_all_by_host(context, host):
+ """All networks for which the given host is the network host."""
+ return IMPL.network_get_all_by_host(context, host)
+
+
def network_get_index(context, network_id):
"""Get non-conflicting index for network."""
return IMPL.network_get_index(context, network_id)
@@ -682,23 +752,6 @@ def network_update(context, network_id, values):
###################
-def project_get_network(context, project_id, associate=True):
- """Return the network associated with the project.
-
- If associate is true, it will attempt to associate a new
- network if one is not found, otherwise it returns None.
-
- """
- return IMPL.project_get_network(context, project_id, associate)
-
-
-def project_get_network_v6(context, project_id):
- return IMPL.project_get_network_v6(context, project_id)
-
-
-###################
-
-
def queue_get_for(context, topic, physical_node_id):
"""Return a channel to send a message to a node with a topic."""
return IMPL.queue_get_for(context, topic, physical_node_id)
@@ -1128,6 +1181,9 @@ def user_update(context, user_id, values):
return IMPL.user_update(context, user_id, values)
+###################
+
+
def project_get(context, id):
"""Get project by id."""
return IMPL.project_get(context, id)
@@ -1168,15 +1224,21 @@ def project_delete(context, project_id):
return IMPL.project_delete(context, project_id)
-###################
+def project_get_networks(context, project_id, associate=True):
+ """Return the network associated with the project.
+ If associate is true, it will attempt to associate a new
+ network if one is not found, otherwise it returns None.
-def host_get_networks(context, host):
- """All networks for which the given host is the network host."""
- return IMPL.host_get_networks(context, host)
+ """
+ return IMPL.project_get_networks(context, project_id, associate)
-##################
+def project_get_networks_v6(context, project_id):
+ return IMPL.project_get_networks_v6(context, project_id)
+
+
+###################
def console_pool_create(context, values):
@@ -1282,7 +1344,7 @@ def zone_create(context, values):
def zone_update(context, zone_id, values):
"""Update a child Zone entry."""
- return IMPL.zone_update(context, values)
+ return IMPL.zone_update(context, zone_id, values)
def zone_delete(context, zone_id):
@@ -1345,3 +1407,24 @@ def agent_build_destroy(context, agent_update_id):
def agent_build_update(context, agent_build_id, values):
"""Update agent build entry."""
IMPL.agent_build_update(context, agent_build_id, values)
+
+
+####################
+
+
+def instance_type_extra_specs_get(context, instance_type_id):
+ """Get all extra specs for an instance type."""
+ return IMPL.instance_type_extra_specs_get(context, instance_type_id)
+
+
+def instance_type_extra_specs_delete(context, instance_type_id, key):
+ """Delete the given extra specs item."""
+ IMPL.instance_type_extra_specs_delete(context, instance_type_id, key)
+
+
+def instance_type_extra_specs_update_or_create(context, instance_type_id,
+ extra_specs):
+ """Create or update instance type extra specs. This adds or modifies the
+ key/value pairs specified in the extra specs dict argument"""
+ IMPL.instance_type_extra_specs_update_or_create(context, instance_type_id,
+ extra_specs)
diff --git a/nova/db/sqlalchemy/api.py b/nova/db/sqlalchemy/api.py
index e2ba73b1c..d575816d0 100644
--- a/nova/db/sqlalchemy/api.py
+++ b/nova/db/sqlalchemy/api.py
@@ -26,6 +26,7 @@ from nova import exception
from nova import flags
from nova import ipv6
from nova import utils
+from nova import log as logging
from nova.db.sqlalchemy import models
from nova.db.sqlalchemy.session import get_session
from sqlalchemy import or_
@@ -37,6 +38,7 @@ from sqlalchemy.sql import func
from sqlalchemy.sql.expression import literal_column
FLAGS = flags.FLAGS
+LOG = logging.getLogger("nova.db.sqlalchemy")
def is_admin_context(context):
@@ -431,12 +433,36 @@ def certificate_update(context, certificate_id, values):
@require_context
-def floating_ip_allocate_address(context, host, project_id):
+def floating_ip_get(context, id):
+ session = get_session()
+ result = None
+ if is_admin_context(context):
+ result = session.query(models.FloatingIp).\
+ options(joinedload('fixed_ip')).\
+ options(joinedload_all('fixed_ip.instance')).\
+ filter_by(id=id).\
+ filter_by(deleted=can_read_deleted(context)).\
+ first()
+ elif is_user_context(context):
+ result = session.query(models.FloatingIp).\
+ options(joinedload('fixed_ip')).\
+ options(joinedload_all('fixed_ip.instance')).\
+ filter_by(project_id=context.project_id).\
+ filter_by(id=id).\
+ filter_by(deleted=False).\
+ first()
+ if not result:
+ raise exception.FloatingIpNotFound(id=id)
+
+ return result
+
+
+@require_context
+def floating_ip_allocate_address(context, project_id):
authorize_project_context(context, project_id)
session = get_session()
with session.begin():
floating_ip_ref = session.query(models.FloatingIp).\
- filter_by(host=host).\
filter_by(fixed_ip_id=None).\
filter_by(project_id=None).\
filter_by(deleted=False).\
@@ -445,7 +471,7 @@ def floating_ip_allocate_address(context, host, project_id):
# NOTE(vish): if with_lockmode isn't supported, as in sqlite,
# then this has concurrency issues
if not floating_ip_ref:
- raise db.NoMoreAddresses()
+ raise exception.NoMoreFloatingIps()
floating_ip_ref['project_id'] = project_id
session.add(floating_ip_ref)
return floating_ip_ref['address']
@@ -463,6 +489,7 @@ def floating_ip_create(context, values):
def floating_ip_count_by_project(context, project_id):
authorize_project_context(context, project_id)
session = get_session()
+ # TODO(tr3buchet): why leave auto_assigned floating IPs out?
return session.query(models.FloatingIp).\
filter_by(project_id=project_id).\
filter_by(auto_assigned=False).\
@@ -494,6 +521,7 @@ def floating_ip_deallocate(context, address):
address,
session=session)
floating_ip_ref['project_id'] = None
+ floating_ip_ref['host'] = None
floating_ip_ref['auto_assigned'] = False
floating_ip_ref.save(session=session)
@@ -542,32 +570,42 @@ def floating_ip_set_auto_assigned(context, address):
@require_admin_context
def floating_ip_get_all(context):
session = get_session()
- return session.query(models.FloatingIp).\
- options(joinedload_all('fixed_ip.instance')).\
- filter_by(deleted=False).\
- all()
+ floating_ip_refs = session.query(models.FloatingIp).\
+ options(joinedload_all('fixed_ip.instance')).\
+ filter_by(deleted=False).\
+ all()
+ if not floating_ip_refs:
+ raise exception.NoFloatingIpsDefined()
+ return floating_ip_refs
@require_admin_context
def floating_ip_get_all_by_host(context, host):
session = get_session()
- return session.query(models.FloatingIp).\
- options(joinedload_all('fixed_ip.instance')).\
- filter_by(host=host).\
- filter_by(deleted=False).\
- all()
+ floating_ip_refs = session.query(models.FloatingIp).\
+ options(joinedload_all('fixed_ip.instance')).\
+ filter_by(host=host).\
+ filter_by(deleted=False).\
+ all()
+ if not floating_ip_refs:
+ raise exception.FloatingIpNotFoundForHost(host=host)
+ return floating_ip_refs
@require_context
def floating_ip_get_all_by_project(context, project_id):
authorize_project_context(context, project_id)
session = get_session()
- return session.query(models.FloatingIp).\
- options(joinedload_all('fixed_ip.instance')).\
- filter_by(project_id=project_id).\
- filter_by(auto_assigned=False).\
- filter_by(deleted=False).\
- all()
+ # TODO(tr3buchet): why do we not want auto_assigned floating IPs here?
+ floating_ip_refs = session.query(models.FloatingIp).\
+ options(joinedload_all('fixed_ip.instance')).\
+ filter_by(project_id=project_id).\
+ filter_by(auto_assigned=False).\
+ filter_by(deleted=False).\
+ all()
+ if not floating_ip_refs:
+ raise exception.FloatingIpNotFoundForProject(project_id=project_id)
+ return floating_ip_refs
@require_context
@@ -577,13 +615,12 @@ def floating_ip_get_by_address(context, address, session=None):
session = get_session()
result = session.query(models.FloatingIp).\
- options(joinedload_all('fixed_ip.network')).\
+ options(joinedload_all('fixed_ip.network')).\
filter_by(address=address).\
filter_by(deleted=can_read_deleted(context)).\
first()
if not result:
- raise exception.FloatingIpNotFound(fixed_ip=address)
-
+ raise exception.FloatingIpNotFoundForAddress(address=address)
return result
@@ -614,7 +651,7 @@ def fixed_ip_associate(context, address, instance_id):
# NOTE(vish): if with_lockmode isn't supported, as in sqlite,
# then this has concurrency issues
if not fixed_ip_ref:
- raise db.NoMoreAddresses()
+ raise exception.NoMoreFixedIps()
fixed_ip_ref.instance = instance
session.add(fixed_ip_ref)
@@ -635,7 +672,7 @@ def fixed_ip_associate_pool(context, network_id, instance_id):
# NOTE(vish): if with_lockmode isn't supported, as in sqlite,
# then this has concurrency issues
if not fixed_ip_ref:
- raise db.NoMoreAddresses()
+ raise exception.NoMoreFixedIps()
if not fixed_ip_ref.network:
fixed_ip_ref.network = network_get(context,
network_id,
@@ -676,9 +713,9 @@ def fixed_ip_disassociate_all_by_timeout(_context, host, time):
filter(models.FixedIp.network_id.in_(inner_q)).\
filter(models.FixedIp.updated_at < time).\
filter(models.FixedIp.instance_id != None).\
- filter_by(allocated=0).\
+ filter_by(allocated=False).\
update({'instance_id': None,
- 'leased': 0,
+ 'leased': False,
'updated_at': utils.utcnow()},
synchronize_session='fetch')
return result
@@ -688,9 +725,11 @@ def fixed_ip_disassociate_all_by_timeout(_context, host, time):
def fixed_ip_get_all(context, session=None):
if not session:
session = get_session()
- result = session.query(models.FixedIp).all()
+ result = session.query(models.FixedIp).\
+ options(joinedload('floating_ips')).\
+ all()
if not result:
- raise exception.NoFloatingIpsDefined()
+ raise exception.NoFixedIpsDefined()
return result
@@ -700,13 +739,14 @@ def fixed_ip_get_all_by_host(context, host=None):
session = get_session()
result = session.query(models.FixedIp).\
- join(models.FixedIp.instance).\
- filter_by(state=1).\
- filter_by(host=host).\
- all()
+ options(joinedload('floating_ips')).\
+ join(models.FixedIp.instance).\
+ filter_by(state=1).\
+ filter_by(host=host).\
+ all()
if not result:
- raise exception.NoFloatingIpsDefinedForHost(host=host)
+ raise exception.FixedIpNotFoundForHost(host=host)
return result
@@ -718,11 +758,12 @@ def fixed_ip_get_by_address(context, address, session=None):
result = session.query(models.FixedIp).\
filter_by(address=address).\
filter_by(deleted=can_read_deleted(context)).\
+ options(joinedload('floating_ips')).\
options(joinedload('network')).\
options(joinedload('instance')).\
first()
if not result:
- raise exception.FloatingIpNotFound(fixed_ip=address)
+ raise exception.FixedIpNotFoundForAddress(address=address)
if is_user_context(context):
authorize_project_context(context, result.instance.project_id)
@@ -731,30 +772,50 @@ def fixed_ip_get_by_address(context, address, session=None):
@require_context
-def fixed_ip_get_instance(context, address):
- fixed_ip_ref = fixed_ip_get_by_address(context, address)
- return fixed_ip_ref.instance
+def fixed_ip_get_by_instance(context, instance_id):
+ session = get_session()
+ rv = session.query(models.FixedIp).\
+ options(joinedload('floating_ips')).\
+ filter_by(instance_id=instance_id).\
+ filter_by(deleted=False).\
+ all()
+ if not rv:
+ raise exception.FixedIpNotFoundForInstance(instance_id=instance_id)
+ return rv
@require_context
-def fixed_ip_get_all_by_instance(context, instance_id):
+def fixed_ip_get_by_virtual_interface(context, vif_id):
session = get_session()
rv = session.query(models.FixedIp).\
- filter_by(instance_id=instance_id).\
- filter_by(deleted=False)
+ options(joinedload('floating_ips')).\
+ filter_by(virtual_interface_id=vif_id).\
+ filter_by(deleted=False).\
+ all()
if not rv:
- raise exception.NoFixedIpsFoundForInstance(instance_id=instance_id)
+ raise exception.FixedIpNotFoundForVirtualInterface(vif_id=vif_id)
return rv
@require_context
+def fixed_ip_get_instance(context, address):
+ fixed_ip_ref = fixed_ip_get_by_address(context, address)
+ return fixed_ip_ref.instance
+
+
+@require_context
def fixed_ip_get_instance_v6(context, address):
session = get_session()
+
+ # convert IPv6 address to mac
mac = ipv6.to_mac(address)
+ # get virtual interface
+ vif_ref = virtual_interface_get_by_address(context, mac)
+
+ # look up instance based on instance_id from vif row
result = session.query(models.Instance).\
- filter_by(mac_address=mac).\
- first()
+ filter_by(id=vif_ref['instance_id'])
return result
@@ -776,6 +837,163 @@ def fixed_ip_update(context, address, values):
###################
+
+
+@require_context
+def virtual_interface_create(context, values):
+ """Create a new virtual interface record in teh database.
+
+ :param values: = dict containing column values
+ """
+ try:
+ vif_ref = models.VirtualInterface()
+ vif_ref.update(values)
+ vif_ref.save()
+ except IntegrityError:
+ raise exception.VirtualInterfaceCreateException()
+
+ return vif_ref
+
+
+@require_context
+def virtual_interface_update(context, vif_id, values):
+ """Update a virtual interface record in the database.
+
+ :param vif_id: = id of virtual interface to update
+ :param values: = values to update
+ """
+ session = get_session()
+ with session.begin():
+ vif_ref = virtual_interface_get(context, vif_id, session=session)
+ vif_ref.update(values)
+ vif_ref.save(session=session)
+ return vif_ref
+
+
+@require_context
+def virtual_interface_get(context, vif_id, session=None):
+ """Gets a virtual interface from the table.
+
+ :param vif_id: = id of the virtual interface
+ """
+ if not session:
+ session = get_session()
+
+ vif_ref = session.query(models.VirtualInterface).\
+ filter_by(id=vif_id).\
+ options(joinedload('network')).\
+ options(joinedload('instance')).\
+ options(joinedload('fixed_ips')).\
+ first()
+ return vif_ref
+
+
+@require_context
+def virtual_interface_get_by_address(context, address):
+ """Gets a virtual interface from the table.
+
+ :param address: = the address of the interface you're looking to get
+ """
+ session = get_session()
+ vif_ref = session.query(models.VirtualInterface).\
+ filter_by(address=address).\
+ options(joinedload('network')).\
+ options(joinedload('instance')).\
+ options(joinedload('fixed_ips')).\
+ first()
+ return vif_ref
+
+
+@require_context
+def virtual_interface_get_by_fixed_ip(context, fixed_ip_id):
+ """Gets the virtual interface fixed_ip is associated with.
+
+ :param fixed_ip_id: = id of the fixed_ip
+ """
+ session = get_session()
+ vif_ref = session.query(models.VirtualInterface).\
+ filter_by(fixed_ip_id=fixed_ip_id).\
+ options(joinedload('network')).\
+ options(joinedload('instance')).\
+ options(joinedload('fixed_ips')).\
+ first()
+ return vif_ref
+
+
+@require_context
+def virtual_interface_get_by_instance(context, instance_id):
+ """Gets all virtual interfaces for instance.
+
+ :param instance_id: = id of the instance to retreive vifs for
+ """
+ session = get_session()
+ vif_refs = session.query(models.VirtualInterface).\
+ filter_by(instance_id=instance_id).\
+ options(joinedload('network')).\
+ options(joinedload('instance')).\
+ options(joinedload('fixed_ips')).\
+ all()
+ return vif_refs
+
+
+@require_context
+def virtual_interface_get_by_instance_and_network(context, instance_id,
+ network_id):
+ """Gets virtual interface for instance that's associated with network."""
+ session = get_session()
+ vif_ref = session.query(models.VirtualInterface).\
+ filter_by(instance_id=instance_id).\
+ filter_by(network_id=network_id).\
+ options(joinedload('network')).\
+ options(joinedload('instance')).\
+ options(joinedload('fixed_ips')).\
+ first()
+ return vif_ref
+
+
+@require_admin_context
+def virtual_interface_get_by_network(context, network_id):
+ """Gets all virtual_interface on network.
+
+ :param network_id: = network to retreive vifs for
+ """
+ session = get_session()
+ vif_refs = session.query(models.VirtualInterface).\
+ filter_by(network_id=network_id).\
+ options(joinedload('network')).\
+ options(joinedload('instance')).\
+ options(joinedload('fixed_ips')).\
+ all()
+ return vif_refs
+
+
+@require_context
+def virtual_interface_delete(context, vif_id):
+ """Delete virtual interface record from teh database.
+
+ :param vif_id: = id of vif to delete
+ """
+ session = get_session()
+ vif_ref = virtual_interface_get(context, vif_id, session)
+ with session.begin():
+ session.delete(vif_ref)
+
+
+@require_context
+def virtual_interface_delete_by_instance(context, instance_id):
+ """Delete virtual interface records that are associated
+ with the instance given by instance_id.
+
+ :param instance_id: = id of instance
+ """
+ vif_refs = virtual_interface_get_by_instance(context, instance_id)
+ for vif_ref in vif_refs:
+ virtual_interface_delete(context, vif_ref['id'])
+
+
+###################
+
+
def _metadata_refs(metadata_dict):
metadata_refs = []
if metadata_dict:
@@ -888,10 +1106,11 @@ def _build_instance_get(context, session=None):
session = get_session()
partial = session.query(models.Instance).\
- options(joinedload_all('fixed_ip.floating_ips')).\
+ options(joinedload_all('fixed_ips.floating_ips')).\
+ options(joinedload_all('fixed_ips.network')).\
+ options(joinedload('virtual_interfaces')).\
options(joinedload_all('security_groups.rules')).\
options(joinedload('volumes')).\
- options(joinedload_all('fixed_ip.network')).\
options(joinedload('metadata')).\
options(joinedload('instance_type'))
@@ -907,9 +1126,10 @@ def _build_instance_get(context, session=None):
def instance_get_all(context):
session = get_session()
return session.query(models.Instance).\
- options(joinedload_all('fixed_ip.floating_ips')).\
+ options(joinedload_all('fixed_ips.floating_ips')).\
+ options(joinedload('virtual_interfaces')).\
options(joinedload('security_groups')).\
- options(joinedload_all('fixed_ip.network')).\
+ options(joinedload_all('fixed_ips.network')).\
options(joinedload('metadata')).\
options(joinedload('instance_type')).\
filter_by(deleted=can_read_deleted(context)).\
@@ -917,12 +1137,31 @@ def instance_get_all(context):
@require_admin_context
-def instance_get_all_by_user(context, user_id):
+def instance_get_active_by_window(context, begin, end=None):
+ """Return instances that were continuously active over the given window"""
session = get_session()
- return session.query(models.Instance).\
+ query = session.query(models.Instance).\
options(joinedload_all('fixed_ip.floating_ips')).\
options(joinedload('security_groups')).\
options(joinedload_all('fixed_ip.network')).\
+ options(joinedload('instance_type')).\
+ filter(models.Instance.launched_at < begin)
+ if end:
+ query = query.filter(or_(models.Instance.terminated_at == None,
+ models.Instance.terminated_at > end))
+ else:
+ query = query.filter(models.Instance.terminated_at == None)
+ return query.all()
+
+
+@require_admin_context
+def instance_get_all_by_user(context, user_id):
+ session = get_session()
+ return session.query(models.Instance).\
+ options(joinedload_all('fixed_ips.floating_ips')).\
+ options(joinedload('virtual_interfaces')).\
+ options(joinedload('security_groups')).\
+ options(joinedload_all('fixed_ips.network')).\
options(joinedload('metadata')).\
options(joinedload('instance_type')).\
filter_by(deleted=can_read_deleted(context)).\
@@ -934,9 +1173,10 @@ def instance_get_all_by_user(context, user_id):
def instance_get_all_by_host(context, host):
session = get_session()
return session.query(models.Instance).\
- options(joinedload_all('fixed_ip.floating_ips')).\
+ options(joinedload_all('fixed_ips.floating_ips')).\
+ options(joinedload('virtual_interfaces')).\
options(joinedload('security_groups')).\
- options(joinedload_all('fixed_ip.network')).\
+ options(joinedload_all('fixed_ips.network')).\
options(joinedload('metadata')).\
options(joinedload('instance_type')).\
filter_by(host=host).\
@@ -950,9 +1190,10 @@ def instance_get_all_by_project(context, project_id):
session = get_session()
return session.query(models.Instance).\
- options(joinedload_all('fixed_ip.floating_ips')).\
+ options(joinedload_all('fixed_ips.floating_ips')).\
+ options(joinedload('virtual_interfaces')).\
options(joinedload('security_groups')).\
- options(joinedload_all('fixed_ip.network')).\
+ options(joinedload_all('fixed_ips.network')).\
options(joinedload('metadata')).\
options(joinedload('instance_type')).\
filter_by(project_id=project_id).\
@@ -966,9 +1207,10 @@ def instance_get_all_by_reservation(context, reservation_id):
if is_admin_context(context):
return session.query(models.Instance).\
- options(joinedload_all('fixed_ip.floating_ips')).\
+ options(joinedload_all('fixed_ips.floating_ips')).\
+ options(joinedload('virtual_interfaces')).\
options(joinedload('security_groups')).\
- options(joinedload_all('fixed_ip.network')).\
+ options(joinedload_all('fixed_ips.network')).\
options(joinedload('metadata')).\
options(joinedload('instance_type')).\
filter_by(reservation_id=reservation_id).\
@@ -976,9 +1218,10 @@ def instance_get_all_by_reservation(context, reservation_id):
all()
elif is_user_context(context):
return session.query(models.Instance).\
- options(joinedload_all('fixed_ip.floating_ips')).\
+ options(joinedload_all('fixed_ips.floating_ips')).\
+ options(joinedload('virtual_interfaces')).\
options(joinedload('security_groups')).\
- options(joinedload_all('fixed_ip.network')).\
+ options(joinedload_all('fixed_ips.network')).\
options(joinedload('metadata')).\
options(joinedload('instance_type')).\
filter_by(project_id=context.project_id).\
@@ -991,7 +1234,8 @@ def instance_get_all_by_reservation(context, reservation_id):
def instance_get_project_vpn(context, project_id):
session = get_session()
return session.query(models.Instance).\
- options(joinedload_all('fixed_ip.floating_ips')).\
+ options(joinedload_all('fixed_ips.floating_ips')).\
+ options(joinedload('virtual_interfaces')).\
options(joinedload('security_groups')).\
options(joinedload_all('fixed_ip.network')).\
options(joinedload('metadata')).\
@@ -1003,38 +1247,53 @@ def instance_get_project_vpn(context, project_id):
@require_context
-def instance_get_fixed_address(context, instance_id):
+def instance_get_fixed_addresses(context, instance_id):
session = get_session()
with session.begin():
instance_ref = instance_get(context, instance_id, session=session)
- if not instance_ref.fixed_ip:
- return None
- return instance_ref.fixed_ip['address']
+ try:
+ fixed_ips = fixed_ip_get_by_instance(context, instance_id)
+ except exception.NotFound:
+ return []
+ return [fixed_ip.address for fixed_ip in fixed_ips]
@require_context
-def instance_get_fixed_address_v6(context, instance_id):
+def instance_get_fixed_addresses_v6(context, instance_id):
session = get_session()
with session.begin():
+ # get instance
instance_ref = instance_get(context, instance_id, session=session)
- network_ref = network_get_by_instance(context, instance_id)
- prefix = network_ref.cidr_v6
- mac = instance_ref.mac_address
+ # assume instance has 1 mac for each network associated with it
+ # get networks associated with instance
+ network_refs = network_get_all_by_instance(context, instance_id)
+ # compile a list of cidr_v6 prefixes sorted by network id
+ prefixes = [ref.cidr_v6 for ref in
+ sorted(network_refs, key=lambda ref: ref.id)]
+ # get vifs associated with instance
+ vif_refs = virtual_interface_get_by_instance(context, instance_ref.id)
+ # compile list of the mac_addresses for vifs sorted by network id
+ macs = [vif_ref['address'] for vif_ref in
+ sorted(vif_refs, key=lambda vif_ref: vif_ref['network_id'])]
+ # get project id from instance
project_id = instance_ref.project_id
- return ipv6.to_global(prefix, mac, project_id)
+ # combine prefixes, macs, and project_id into (prefix,mac,p_id) tuples
+ prefix_mac_tuples = zip(prefixes, macs, [project_id for m in macs])
+ # return list containing ipv6 address for each tuple
+ return [ipv6.to_global_ipv6(*t) for t in prefix_mac_tuples]
@require_context
def instance_get_floating_address(context, instance_id):
- session = get_session()
- with session.begin():
- instance_ref = instance_get(context, instance_id, session=session)
- if not instance_ref.fixed_ip:
- return None
- if not instance_ref.fixed_ip.floating_ips:
- return None
- # NOTE(vish): this just returns the first floating ip
- return instance_ref.fixed_ip.floating_ips[0]['address']
+ fixed_ip_refs = fixed_ip_get_by_instance(context, instance_id)
+ if not fixed_ip_refs:
+ return None
+ # NOTE(tr3buchet): this only gets the first fixed_ip
+ # won't find floating ips associated with other fixed_ips
+ if not fixed_ip_refs[0].floating_ips:
+ return None
+ # NOTE(vish): this just returns the first floating ip
+ return fixed_ip_refs[0].floating_ips[0]['address']
@require_admin_context
@@ -1199,20 +1458,52 @@ def key_pair_get_all_by_user(context, user_id):
@require_admin_context
-def network_associate(context, project_id):
+def network_associate(context, project_id, force=False):
+ """Associate a project with a network.
+
+ called by project_get_networks under certain conditions
+ and network manager add_network_to_project()
+
+ only associates projects with networks that have configured hosts
+
+ only associate if the project doesn't already have a network
+ or if force is True
+
+ force solves race condition where a fresh project has multiple instance
+ builds simultaneosly picked up by multiple network hosts which attempt
+ to associate the project with multiple networks
+ force should only be used as a direct consequence of user request
+ all automated requests should not use force
+ """
session = get_session()
with session.begin():
- network_ref = session.query(models.Network).\
- filter_by(deleted=False).\
- filter_by(project_id=None).\
- with_lockmode('update').\
- first()
- # NOTE(vish): if with_lockmode isn't supported, as in sqlite,
- # then this has concurrency issues
- if not network_ref:
- raise db.NoMoreNetworks()
- network_ref['project_id'] = project_id
- session.add(network_ref)
+
+ def network_query(project_filter):
+ return session.query(models.Network).\
+ filter_by(deleted=False).\
+ filter(models.Network.host != None).\
+ filter_by(project_id=project_filter).\
+ with_lockmode('update').\
+ first()
+
+ if not force:
+ # find out if project has a network
+ network_ref = network_query(project_id)
+
+ if force or not network_ref:
+ # in force mode or project doesn't have a network so assocaite
+ # with a new network
+
+ # get new network
+ network_ref = network_query(None)
+ if not network_ref:
+ raise db.NoMoreNetworks()
+
+ # associate with network
+ # NOTE(vish): if with_lockmode isn't supported, as in sqlite,
+ # then this has concurrency issues
+ network_ref['project_id'] = project_id
+ session.add(network_ref)
return network_ref
@@ -1315,7 +1606,8 @@ def network_get(context, network_id, session=None):
@require_admin_context
def network_get_all(context):
session = get_session()
- result = session.query(models.Network)
+ result = session.query(models.Network).\
+ filter_by(deleted=False).all()
if not result:
raise exception.NoNetworksFound()
return result
@@ -1333,6 +1625,7 @@ def network_get_associated_fixed_ips(context, network_id):
options(joinedload_all('instance')).\
filter_by(network_id=network_id).\
filter(models.FixedIp.instance_id != None).\
+ filter(models.FixedIp.virtual_interface_id != None).\
filter_by(deleted=False).\
all()
@@ -1363,6 +1656,8 @@ def network_get_by_cidr(context, cidr):
@require_admin_context
def network_get_by_instance(_context, instance_id):
+ # note this uses fixed IP to get to instance
+ # only works for networks the instance has an IP from
session = get_session()
rv = session.query(models.Network).\
filter_by(deleted=False).\
@@ -1382,13 +1677,24 @@ def network_get_all_by_instance(_context, instance_id):
filter_by(deleted=False).\
join(models.Network.fixed_ips).\
filter_by(instance_id=instance_id).\
- filter_by(deleted=False)
+ filter_by(deleted=False).\
+ all()
if not rv:
raise exception.NetworkNotFoundForInstance(instance_id=instance_id)
return rv
@require_admin_context
+def network_get_all_by_host(context, host):
+ session = get_session()
+ with session.begin():
+ return session.query(models.Network).\
+ filter_by(deleted=False).\
+ filter_by(host=host).\
+ all()
+
+
+@require_admin_context
def network_set_host(context, network_id, host_id):
session = get_session()
with session.begin():
@@ -1421,37 +1727,6 @@ def network_update(context, network_id, values):
###################
-@require_context
-def project_get_network(context, project_id, associate=True):
- session = get_session()
- result = session.query(models.Network).\
- filter_by(project_id=project_id).\
- filter_by(deleted=False).\
- first()
- if not result:
- if not associate:
- return None
- try:
- return network_associate(context, project_id)
- except IntegrityError:
- # NOTE(vish): We hit this if there is a race and two
- # processes are attempting to allocate the
- # network at the same time
- result = session.query(models.Network).\
- filter_by(project_id=project_id).\
- filter_by(deleted=False).\
- first()
- return result
-
-
-@require_context
-def project_get_network_v6(context, project_id):
- return project_get_network(context, project_id)
-
-
-###################
-
-
def queue_get_for(_context, topic, physical_node_id):
# FIXME(ja): this should be servername?
return "%s.%s" % (topic, physical_node_id)
@@ -2301,6 +2576,73 @@ def user_get_all(context):
all()
+def user_get_roles(context, user_id):
+ session = get_session()
+ with session.begin():
+ user_ref = user_get(context, user_id, session=session)
+ return [role.role for role in user_ref['roles']]
+
+
+def user_get_roles_for_project(context, user_id, project_id):
+ session = get_session()
+ with session.begin():
+ res = session.query(models.UserProjectRoleAssociation).\
+ filter_by(user_id=user_id).\
+ filter_by(project_id=project_id).\
+ all()
+ return [association.role for association in res]
+
+
+def user_remove_project_role(context, user_id, project_id, role):
+ session = get_session()
+ with session.begin():
+ session.query(models.UserProjectRoleAssociation).\
+ filter_by(user_id=user_id).\
+ filter_by(project_id=project_id).\
+ filter_by(role=role).\
+ delete()
+
+
+def user_remove_role(context, user_id, role):
+ session = get_session()
+ with session.begin():
+ res = session.query(models.UserRoleAssociation).\
+ filter_by(user_id=user_id).\
+ filter_by(role=role).\
+ all()
+ for role in res:
+ session.delete(role)
+
+
+def user_add_role(context, user_id, role):
+ session = get_session()
+ with session.begin():
+ user_ref = user_get(context, user_id, session=session)
+ models.UserRoleAssociation(user=user_ref, role=role).\
+ save(session=session)
+
+
+def user_add_project_role(context, user_id, project_id, role):
+ session = get_session()
+ with session.begin():
+ user_ref = user_get(context, user_id, session=session)
+ project_ref = project_get(context, project_id, session=session)
+ models.UserProjectRoleAssociation(user_id=user_ref['id'],
+ project_id=project_ref['id'],
+ role=role).save(session=session)
+
+
+def user_update(context, user_id, values):
+ session = get_session()
+ with session.begin():
+ user_ref = user_get(context, user_id, session=session)
+ user_ref.update(values)
+ user_ref.save(session=session)
+
+
+###################
+
+
def project_create(_context, values):
project_ref = models.Project()
project_ref.update(values)
@@ -2364,14 +2706,6 @@ def project_remove_member(context, project_id, user_id):
project.save(session=session)
-def user_update(context, user_id, values):
- session = get_session()
- with session.begin():
- user_ref = user_get(context, user_id, session=session)
- user_ref.update(values)
- user_ref.save(session=session)
-
-
def project_update(context, project_id, values):
session = get_session()
with session.begin():
@@ -2393,73 +2727,26 @@ def project_delete(context, id):
session.delete(project_ref)
-def user_get_roles(context, user_id):
- session = get_session()
- with session.begin():
- user_ref = user_get(context, user_id, session=session)
- return [role.role for role in user_ref['roles']]
-
-
-def user_get_roles_for_project(context, user_id, project_id):
- session = get_session()
- with session.begin():
- res = session.query(models.UserProjectRoleAssociation).\
- filter_by(user_id=user_id).\
- filter_by(project_id=project_id).\
- all()
- return [association.role for association in res]
-
-
-def user_remove_project_role(context, user_id, project_id, role):
- session = get_session()
- with session.begin():
- session.query(models.UserProjectRoleAssociation).\
- filter_by(user_id=user_id).\
- filter_by(project_id=project_id).\
- filter_by(role=role).\
- delete()
-
-
-def user_remove_role(context, user_id, role):
- session = get_session()
- with session.begin():
- res = session.query(models.UserRoleAssociation).\
- filter_by(user_id=user_id).\
- filter_by(role=role).\
- all()
- for role in res:
- session.delete(role)
-
-
-def user_add_role(context, user_id, role):
- session = get_session()
- with session.begin():
- user_ref = user_get(context, user_id, session=session)
- models.UserRoleAssociation(user=user_ref, role=role).\
- save(session=session)
-
-
-def user_add_project_role(context, user_id, project_id, role):
+@require_context
+def project_get_networks(context, project_id, associate=True):
+ # NOTE(tr3buchet): as before this function will associate
+ # a project with a network if it doesn't have one and
+ # associate is true
session = get_session()
- with session.begin():
- user_ref = user_get(context, user_id, session=session)
- project_ref = project_get(context, project_id, session=session)
- models.UserProjectRoleAssociation(user_id=user_ref['id'],
- project_id=project_ref['id'],
- role=role).save(session=session)
-
+ result = session.query(models.Network).\
+ filter_by(project_id=project_id).\
+ filter_by(deleted=False).all()
-###################
+ if not result:
+ if not associate:
+ return []
+ return [network_associate(context, project_id)]
+ return result
-@require_admin_context
-def host_get_networks(context, host):
- session = get_session()
- with session.begin():
- return session.query(models.Network).\
- filter_by(deleted=False).\
- filter_by(host=host).\
- all()
+@require_context
+def project_get_networks_v6(context, project_id):
+ return project_get_networks(context, project_id)
###################
@@ -2614,7 +2901,22 @@ def console_get(context, console_id, instance_id=None):
@require_admin_context
def instance_type_create(_context, values):
+ """Create a new instance type. In order to pass in extra specs,
+ the values dict should contain a 'extra_specs' key/value pair:
+
+ {'extra_specs' : {'k1': 'v1', 'k2': 'v2', ...}}
+
+ """
try:
+ specs = values.get('extra_specs')
+ specs_refs = []
+ if specs:
+ for k, v in specs.iteritems():
+ specs_ref = models.InstanceTypeExtraSpecs()
+ specs_ref['key'] = k
+ specs_ref['value'] = v
+ specs_refs.append(specs_ref)
+ values['extra_specs'] = specs_refs
instance_type_ref = models.InstanceTypes()
instance_type_ref.update(values)
instance_type_ref.save()
@@ -2623,6 +2925,25 @@ def instance_type_create(_context, values):
return instance_type_ref
+def _dict_with_extra_specs(inst_type_query):
+ """Takes an instance type query returned by sqlalchemy
+ and returns it as a dictionary, converting the extra_specs
+ entry from a list of dicts:
+
+ 'extra_specs' : [{'key': 'k1', 'value': 'v1', ...}, ...]
+
+ to a single dict:
+
+ 'extra_specs' : {'k1': 'v1'}
+
+ """
+ inst_type_dict = dict(inst_type_query)
+ extra_specs = dict([(x['key'], x['value']) for x in \
+ inst_type_query['extra_specs']])
+ inst_type_dict['extra_specs'] = extra_specs
+ return inst_type_dict
+
+
@require_context
def instance_type_get_all(context, inactive=False):
"""
@@ -2631,20 +2952,20 @@ def instance_type_get_all(context, inactive=False):
session = get_session()
if inactive:
inst_types = session.query(models.InstanceTypes).\
+ options(joinedload('extra_specs')).\
order_by("name").\
all()
else:
inst_types = session.query(models.InstanceTypes).\
+ options(joinedload('extra_specs')).\
filter_by(deleted=False).\
order_by("name").\
all()
+ inst_dict = {}
if inst_types:
- inst_dict = {}
for i in inst_types:
- inst_dict[i['name']] = dict(i)
- return inst_dict
- else:
- raise exception.NoInstanceTypesFound()
+ inst_dict[i['name']] = _dict_with_extra_specs(i)
+ return inst_dict
@require_context
@@ -2652,12 +2973,14 @@ def instance_type_get_by_id(context, id):
"""Returns a dict describing specific instance_type"""
session = get_session()
inst_type = session.query(models.InstanceTypes).\
+ options(joinedload('extra_specs')).\
filter_by(id=id).\
first()
+
if not inst_type:
raise exception.InstanceTypeNotFound(instance_type=id)
else:
- return dict(inst_type)
+ return _dict_with_extra_specs(inst_type)
@require_context
@@ -2665,12 +2988,13 @@ def instance_type_get_by_name(context, name):
"""Returns a dict describing specific instance_type"""
session = get_session()
inst_type = session.query(models.InstanceTypes).\
+ options(joinedload('extra_specs')).\
filter_by(name=name).\
first()
if not inst_type:
raise exception.InstanceTypeNotFoundByName(instance_type_name=name)
else:
- return dict(inst_type)
+ return _dict_with_extra_specs(inst_type)
@require_context
@@ -2678,12 +3002,13 @@ def instance_type_get_by_flavor_id(context, id):
"""Returns a dict describing specific flavor_id"""
session = get_session()
inst_type = session.query(models.InstanceTypes).\
+ options(joinedload('extra_specs')).\
filter_by(flavorid=int(id)).\
first()
if not inst_type:
raise exception.FlavorNotFound(flavor_id=id)
else:
- return dict(inst_type)
+ return _dict_with_extra_specs(inst_type)
@require_admin_context
@@ -2732,7 +3057,7 @@ def zone_update(context, zone_id, values):
if not zone:
raise exception.ZoneNotFound(zone_id=zone_id)
zone.update(values)
- zone.save()
+ zone.save(session=session)
return zone
@@ -2851,6 +3176,9 @@ def instance_metadata_update_or_create(context, instance_id, metadata):
return metadata
+####################
+
+
@require_admin_context
def agent_build_create(context, values):
agent_build_ref = models.AgentBuild()
@@ -2900,3 +3228,70 @@ def agent_build_update(context, agent_build_id, values):
first()
agent_build_ref.update(values)
agent_build_ref.save(session=session)
+
+
+####################
+
+
+@require_context
+def instance_type_extra_specs_get(context, instance_type_id):
+ session = get_session()
+
+ spec_results = session.query(models.InstanceTypeExtraSpecs).\
+ filter_by(instance_type_id=instance_type_id).\
+ filter_by(deleted=False).\
+ all()
+
+ spec_dict = {}
+ for i in spec_results:
+ spec_dict[i['key']] = i['value']
+ return spec_dict
+
+
+@require_context
+def instance_type_extra_specs_delete(context, instance_type_id, key):
+ session = get_session()
+ session.query(models.InstanceTypeExtraSpecs).\
+ filter_by(instance_type_id=instance_type_id).\
+ filter_by(key=key).\
+ filter_by(deleted=False).\
+ update({'deleted': True,
+ 'deleted_at': utils.utcnow(),
+ 'updated_at': literal_column('updated_at')})
+
+
+@require_context
+def instance_type_extra_specs_get_item(context, instance_type_id, key):
+ session = get_session()
+
+ sppec_result = session.query(models.InstanceTypeExtraSpecs).\
+ filter_by(instance_type_id=instance_type_id).\
+ filter_by(key=key).\
+ filter_by(deleted=False).\
+ first()
+
+ if not spec_result:
+ raise exception.\
+ InstanceTypeExtraSpecsNotFound(extra_specs_key=key,
+ instance_type_id=instance_type_id)
+ return spec_result
+
+
+@require_context
+def instance_type_extra_specs_update_or_create(context, instance_type_id,
+ specs):
+ session = get_session()
+ spec_ref = None
+ for key, value in specs.iteritems():
+ try:
+ spec_ref = instance_type_extra_specs_get_item(context,
+ instance_type_id,
+ key,
+ session)
+ except:
+ spec_ref = models.InstanceTypeExtraSpecs()
+ spec_ref.update({"key": key, "value": value,
+ "instance_type_id": instance_type_id,
+ "deleted": 0})
+ spec_ref.save(session=session)
+ return specs
diff --git a/nova/db/sqlalchemy/migrate_repo/versions/027_add_provider_firewall_rules.py b/nova/db/sqlalchemy/migrate_repo/versions/027_add_provider_firewall_rules.py
index 5aa30f7a8..cb3c73170 100644
--- a/nova/db/sqlalchemy/migrate_repo/versions/027_add_provider_firewall_rules.py
+++ b/nova/db/sqlalchemy/migrate_repo/versions/027_add_provider_firewall_rules.py
@@ -58,8 +58,7 @@ provider_fw_rules = Table('provider_fw_rules', meta,
Column('to_port', Integer()),
Column('cidr',
String(length=255, convert_unicode=False, assert_unicode=None,
- unicode_error=None, _warn_on_bytestring=False))
- )
+ unicode_error=None, _warn_on_bytestring=False)))
def upgrade(migrate_engine):
diff --git a/nova/db/sqlalchemy/migrate_repo/versions/028_add_instance_type_extra_specs.py b/nova/db/sqlalchemy/migrate_repo/versions/028_add_instance_type_extra_specs.py
new file mode 100644
index 000000000..f26ad6d2c
--- /dev/null
+++ b/nova/db/sqlalchemy/migrate_repo/versions/028_add_instance_type_extra_specs.py
@@ -0,0 +1,67 @@
+# vim: tabstop=4 shiftwidth=4 softtabstop=4
+
+# Copyright 2011 University of Southern California
+#
+# Licensed under the Apache License, Version 2.0 (the "License"); you may
+# not use this file except in compliance with the License. You may obtain
+# a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations
+# under the License.
+
+from sqlalchemy import Boolean, Column, DateTime, ForeignKey, Integer
+from sqlalchemy import MetaData, String, Table
+from nova import log as logging
+
+meta = MetaData()
+
+# Just for the ForeignKey and column creation to succeed, these are not the
+# actual definitions of instances or services.
+instance_types = Table('instance_types', meta,
+ Column('id', Integer(), primary_key=True, nullable=False),
+ )
+
+#
+# New Tables
+#
+
+instance_type_extra_specs_table = Table('instance_type_extra_specs', meta,
+ Column('created_at', DateTime(timezone=False)),
+ Column('updated_at', DateTime(timezone=False)),
+ Column('deleted_at', DateTime(timezone=False)),
+ Column('deleted', Boolean(create_constraint=True, name=None)),
+ Column('id', Integer(), primary_key=True, nullable=False),
+ Column('instance_type_id',
+ Integer(),
+ ForeignKey('instance_types.id'),
+ nullable=False),
+ Column('key',
+ String(length=255, convert_unicode=False, assert_unicode=None,
+ unicode_error=None, _warn_on_bytestring=False)),
+ Column('value',
+ String(length=255, convert_unicode=False, assert_unicode=None,
+ unicode_error=None, _warn_on_bytestring=False)))
+
+
+def upgrade(migrate_engine):
+ # Upgrade operations go here. Don't create your own engine;
+ # bind migrate_engine to your metadata
+ meta.bind = migrate_engine
+ for table in (instance_type_extra_specs_table, ):
+ try:
+ table.create()
+ except Exception:
+ logging.info(repr(table))
+ logging.exception('Exception while creating table')
+ raise
+
+
+def downgrade(migrate_engine):
+ # Operations to reverse the above upgrade go here.
+ for table in (instance_type_extra_specs_table, ):
+ table.drop()
diff --git a/nova/db/sqlalchemy/migrate_repo/versions/029_add_zone_weight_offsets.py b/nova/db/sqlalchemy/migrate_repo/versions/029_add_zone_weight_offsets.py
new file mode 100644
index 000000000..1b7871e5f
--- /dev/null
+++ b/nova/db/sqlalchemy/migrate_repo/versions/029_add_zone_weight_offsets.py
@@ -0,0 +1,38 @@
+# Copyright 2011 OpenStack LLC.
+#
+# Licensed under the Apache License, Version 2.0 (the "License"); you may
+# not use this file except in compliance with the License. You may obtain
+# a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations
+# under the License.
+
+from sqlalchemy import Column, Float, Integer, MetaData, Table
+
+meta = MetaData()
+
+zones = Table('zones', meta,
+ Column('id', Integer(), primary_key=True, nullable=False),
+ )
+
+weight_offset = Column('weight_offset', Float(), default=0.0)
+weight_scale = Column('weight_scale', Float(), default=1.0)
+
+
+def upgrade(migrate_engine):
+ meta.bind = migrate_engine
+
+ zones.create_column(weight_offset)
+ zones.create_column(weight_scale)
+
+
+def downgrade(migrate_engine):
+ meta.bind = migrate_engine
+
+ zones.drop_column(weight_offset)
+ zones.drop_column(weight_scale)
diff --git a/nova/db/sqlalchemy/migrate_repo/versions/030_multi_nic.py b/nova/db/sqlalchemy/migrate_repo/versions/030_multi_nic.py
new file mode 100644
index 000000000..4a117bb11
--- /dev/null
+++ b/nova/db/sqlalchemy/migrate_repo/versions/030_multi_nic.py
@@ -0,0 +1,125 @@
+# Copyright 2011 OpenStack LLC.
+# All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License"); you may
+# not use this file except in compliance with the License. You may obtain
+# a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations
+# under the License.
+
+import datetime
+
+from sqlalchemy import *
+from migrate import *
+
+from nova import log as logging
+from nova import utils
+
+meta = MetaData()
+
+# virtual interface table to add to DB
+virtual_interfaces = Table('virtual_interfaces', meta,
+ Column('created_at', DateTime(timezone=False),
+ default=utils.utcnow()),
+ Column('updated_at', DateTime(timezone=False),
+ onupdate=utils.utcnow()),
+ Column('deleted_at', DateTime(timezone=False)),
+ Column('deleted', Boolean(create_constraint=True, name=None)),
+ Column('id', Integer(), primary_key=True, nullable=False),
+ Column('address',
+ String(length=255, convert_unicode=False, assert_unicode=None,
+ unicode_error=None, _warn_on_bytestring=False),
+ unique=True),
+ Column('network_id',
+ Integer(),
+ ForeignKey('networks.id')),
+ Column('instance_id',
+ Integer(),
+ ForeignKey('instances.id'),
+ nullable=False),
+ mysql_engine='InnoDB')
+
+
+# bridge_interface column to add to networks table
+interface = Column('bridge_interface',
+ String(length=255, convert_unicode=False,
+ assert_unicode=None, unicode_error=None,
+ _warn_on_bytestring=False))
+
+
+# virtual interface id column to add to fixed_ips table
+# foreignkey added in next migration
+virtual_interface_id = Column('virtual_interface_id',
+ Integer())
+
+
+def upgrade(migrate_engine):
+ meta.bind = migrate_engine
+
+ # grab tables and (column for dropping later)
+ instances = Table('instances', meta, autoload=True)
+ networks = Table('networks', meta, autoload=True)
+ fixed_ips = Table('fixed_ips', meta, autoload=True)
+ c = instances.columns['mac_address']
+
+ # add interface column to networks table
+ # values will have to be set manually before running nova
+ try:
+ networks.create_column(interface)
+ except Exception:
+ logging.error(_("interface column not added to networks table"))
+ raise
+
+ # create virtual_interfaces table
+ try:
+ virtual_interfaces.create()
+ except Exception:
+ logging.error(_("Table |%s| not created!"), repr(virtual_interfaces))
+ raise
+
+ # add virtual_interface_id column to fixed_ips table
+ try:
+ fixed_ips.create_column(virtual_interface_id)
+ except Exception:
+ logging.error(_("VIF column not added to fixed_ips table"))
+ raise
+
+ # populate the virtual_interfaces table
+ # extract data from existing instance and fixed_ip tables
+ s = select([instances.c.id, instances.c.mac_address,
+ fixed_ips.c.network_id],
+ fixed_ips.c.instance_id == instances.c.id)
+ keys = ('instance_id', 'address', 'network_id')
+ join_list = [dict(zip(keys, row)) for row in s.execute()]
+ logging.debug(_("join list for moving mac_addresses |%s|"), join_list)
+
+ # insert data into the table
+ if join_list:
+ i = virtual_interfaces.insert()
+ i.execute(join_list)
+
+ # populate the fixed_ips virtual_interface_id column
+ s = select([fixed_ips.c.id, fixed_ips.c.instance_id],
+ fixed_ips.c.instance_id != None)
+
+ for row in s.execute():
+ m = select([virtual_interfaces.c.id]).\
+ where(virtual_interfaces.c.instance_id == row['instance_id']).\
+ as_scalar()
+ u = fixed_ips.update().values(virtual_interface_id=m).\
+ where(fixed_ips.c.id == row['id'])
+ u.execute()
+
+ # drop the mac_address column from instances
+ c.drop()
+
+
+def downgrade(migrate_engine):
+ logging.error(_("Can't downgrade without losing data"))
+ raise Exception
diff --git a/nova/db/sqlalchemy/migrate_repo/versions/031_fk_fixed_ips_virtual_interface_id.py b/nova/db/sqlalchemy/migrate_repo/versions/031_fk_fixed_ips_virtual_interface_id.py
new file mode 100644
index 000000000..56e927717
--- /dev/null
+++ b/nova/db/sqlalchemy/migrate_repo/versions/031_fk_fixed_ips_virtual_interface_id.py
@@ -0,0 +1,56 @@
+# Copyright 2011 OpenStack LLC.
+# All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License"); you may
+# not use this file except in compliance with the License. You may obtain
+# a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations
+# under the License.
+
+import datetime
+
+from sqlalchemy import *
+from migrate import *
+
+from nova import log as logging
+from nova import utils
+
+meta = MetaData()
+
+
+def upgrade(migrate_engine):
+ meta.bind = migrate_engine
+ dialect = migrate_engine.url.get_dialect().name
+
+ # grab tables
+ fixed_ips = Table('fixed_ips', meta, autoload=True)
+ virtual_interfaces = Table('virtual_interfaces', meta, autoload=True)
+
+ # add foreignkey if not sqlite
+ try:
+ if not dialect.startswith('sqlite'):
+ ForeignKeyConstraint(columns=[fixed_ips.c.virtual_interface_id],
+ refcolumns=[virtual_interfaces.c.id]).create()
+ except Exception:
+ logging.error(_("foreign key constraint couldn't be added"))
+ raise
+
+
+def downgrade(migrate_engine):
+ meta.bind = migrate_engine
+ dialect = migrate_engine.url.get_dialect().name
+
+ # drop foreignkey if not sqlite
+ try:
+ if not dialect.startswith('sqlite'):
+ ForeignKeyConstraint(columns=[fixed_ips.c.virtual_interface_id],
+ refcolumns=[virtual_interfaces.c.id]).drop()
+ except Exception:
+ logging.error(_("foreign key constraint couldn't be dropped"))
+ raise
diff --git a/nova/db/sqlalchemy/migrate_repo/versions/031_sqlite_downgrade.sql b/nova/db/sqlalchemy/migrate_repo/versions/031_sqlite_downgrade.sql
new file mode 100644
index 000000000..c1d26b180
--- /dev/null
+++ b/nova/db/sqlalchemy/migrate_repo/versions/031_sqlite_downgrade.sql
@@ -0,0 +1,48 @@
+BEGIN TRANSACTION;
+
+ CREATE TEMPORARY TABLE fixed_ips_backup (
+ id INTEGER NOT NULL,
+ address VARCHAR(255),
+ virtual_interface_id INTEGER,
+ network_id INTEGER,
+ instance_id INTEGER,
+ allocated BOOLEAN default FALSE,
+ leased BOOLEAN default FALSE,
+ reserved BOOLEAN default FALSE,
+ created_at DATETIME NOT NULL,
+ updated_at DATETIME,
+ deleted_at DATETIME,
+ deleted BOOLEAN NOT NULL,
+ PRIMARY KEY (id),
+ FOREIGN KEY(virtual_interface_id) REFERENCES virtual_interfaces (id)
+ );
+
+ INSERT INTO fixed_ips_backup
+ SELECT id, address, virtual_interface_id, network_id, instance_id, allocated, leased, reserved, created_at, updated_at, deleted_at, deleted
+ FROM fixed_ips;
+
+ DROP TABLE fixed_ips;
+
+ CREATE TABLE fixed_ips (
+ id INTEGER NOT NULL,
+ address VARCHAR(255),
+ virtual_interface_id INTEGER,
+ network_id INTEGER,
+ instance_id INTEGER,
+ allocated BOOLEAN default FALSE,
+ leased BOOLEAN default FALSE,
+ reserved BOOLEAN default FALSE,
+ created_at DATETIME NOT NULL,
+ updated_at DATETIME,
+ deleted_at DATETIME,
+ deleted BOOLEAN NOT NULL,
+ PRIMARY KEY (id)
+ );
+
+ INSERT INTO fixed_ips
+ SELECT id, address, virtual_interface_id, network_id, instance_id, allocated, leased, reserved, created_at, updated_at, deleted_at, deleted
+ FROM fixed_ips;
+
+ DROP TABLE fixed_ips_backup;
+
+COMMIT;
diff --git a/nova/db/sqlalchemy/migrate_repo/versions/031_sqlite_upgrade.sql b/nova/db/sqlalchemy/migrate_repo/versions/031_sqlite_upgrade.sql
new file mode 100644
index 000000000..2a9362545
--- /dev/null
+++ b/nova/db/sqlalchemy/migrate_repo/versions/031_sqlite_upgrade.sql
@@ -0,0 +1,48 @@
+BEGIN TRANSACTION;
+
+ CREATE TEMPORARY TABLE fixed_ips_backup (
+ id INTEGER NOT NULL,
+ address VARCHAR(255),
+ virtual_interface_id INTEGER,
+ network_id INTEGER,
+ instance_id INTEGER,
+ allocated BOOLEAN default FALSE,
+ leased BOOLEAN default FALSE,
+ reserved BOOLEAN default FALSE,
+ created_at DATETIME NOT NULL,
+ updated_at DATETIME,
+ deleted_at DATETIME,
+ deleted BOOLEAN NOT NULL,
+ PRIMARY KEY (id)
+ );
+
+ INSERT INTO fixed_ips_backup
+ SELECT id, address, virtual_interface_id, network_id, instance_id, allocated, leased, reserved, created_at, updated_at, deleted_at, deleted
+ FROM fixed_ips;
+
+ DROP TABLE fixed_ips;
+
+ CREATE TABLE fixed_ips (
+ id INTEGER NOT NULL,
+ address VARCHAR(255),
+ virtual_interface_id INTEGER,
+ network_id INTEGER,
+ instance_id INTEGER,
+ allocated BOOLEAN default FALSE,
+ leased BOOLEAN default FALSE,
+ reserved BOOLEAN default FALSE,
+ created_at DATETIME NOT NULL,
+ updated_at DATETIME,
+ deleted_at DATETIME,
+ deleted BOOLEAN NOT NULL,
+ PRIMARY KEY (id),
+ FOREIGN KEY(virtual_interface_id) REFERENCES virtual_interfaces (id)
+ );
+
+ INSERT INTO fixed_ips
+ SELECT id, address, virtual_interface_id, network_id, instance_id, allocated, leased, reserved, created_at, updated_at, deleted_at, deleted
+ FROM fixed_ips;
+
+ DROP TABLE fixed_ips_backup;
+
+COMMIT;
diff --git a/nova/db/sqlalchemy/models.py b/nova/db/sqlalchemy/models.py
index a943a56fc..1bcc8eaec 100644
--- a/nova/db/sqlalchemy/models.py
+++ b/nova/db/sqlalchemy/models.py
@@ -21,7 +21,7 @@ SQLAlchemy models for nova data.
from sqlalchemy.orm import relationship, backref, object_mapper
from sqlalchemy import Column, Integer, String, schema
-from sqlalchemy import ForeignKey, DateTime, Boolean, Text
+from sqlalchemy import ForeignKey, DateTime, Boolean, Text, Float
from sqlalchemy.exc import IntegrityError
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.schema import ForeignKeyConstraint
@@ -209,12 +209,12 @@ class Instance(BASE, NovaBase):
hostname = Column(String(255))
host = Column(String(255)) # , ForeignKey('hosts.id'))
+ # aka flavor_id
instance_type_id = Column(Integer)
user_data = Column(Text)
reservation_id = Column(String(255))
- mac_address = Column(String(255))
scheduled_at = Column(DateTime)
launched_at = Column(DateTime)
@@ -550,6 +550,7 @@ class Network(BASE, NovaBase):
netmask_v6 = Column(String(255))
netmask = Column(String(255))
bridge = Column(String(255))
+ bridge_interface = Column(String(255))
gateway = Column(String(255))
broadcast = Column(String(255))
dns = Column(String(255))
@@ -560,26 +561,21 @@ class Network(BASE, NovaBase):
vpn_private_address = Column(String(255))
dhcp_start = Column(String(255))
- # NOTE(vish): The unique constraint below helps avoid a race condition
- # when associating a network, but it also means that we
- # can't associate two networks with one project.
- project_id = Column(String(255), unique=True)
+ project_id = Column(String(255))
host = Column(String(255)) # , ForeignKey('hosts.id'))
-class AuthToken(BASE, NovaBase):
- """Represents an authorization token for all API transactions.
-
- Fields are a string representing the actual token and a user id for
- mapping to the actual user
+class VirtualInterface(BASE, NovaBase):
+ """Represents a virtual interface on an instance."""
+ __tablename__ = 'virtual_interfaces'
+ id = Column(Integer, primary_key=True)
+ address = Column(String(255), unique=True)
+ network_id = Column(Integer, ForeignKey('networks.id'))
+ network = relationship(Network, backref=backref('virtual_interfaces'))
- """
- __tablename__ = 'auth_tokens'
- token_hash = Column(String(255), primary_key=True)
- user_id = Column(String(255))
- server_management_url = Column(String(255))
- storage_url = Column(String(255))
- cdn_management_url = Column(String(255))
+ # TODO(tr3buchet): cut the cord, removed foreign key and backrefs
+ instance_id = Column(Integer, ForeignKey('instances.id'), nullable=False)
+ instance = relationship(Instance, backref=backref('virtual_interfaces'))
# TODO(vish): can these both come from the same baseclass?
@@ -590,18 +586,57 @@ class FixedIp(BASE, NovaBase):
address = Column(String(255))
network_id = Column(Integer, ForeignKey('networks.id'), nullable=True)
network = relationship(Network, backref=backref('fixed_ips'))
+ virtual_interface_id = Column(Integer, ForeignKey('virtual_interfaces.id'),
+ nullable=True)
+ virtual_interface = relationship(VirtualInterface,
+ backref=backref('fixed_ips'))
instance_id = Column(Integer, ForeignKey('instances.id'), nullable=True)
instance = relationship(Instance,
- backref=backref('fixed_ip', uselist=False),
+ backref=backref('fixed_ips'),
foreign_keys=instance_id,
primaryjoin='and_('
'FixedIp.instance_id == Instance.id,'
'FixedIp.deleted == False)')
+ # associated means that a fixed_ip has its instance_id column set
+ # allocated means that a fixed_ip has a its virtual_interface_id column set
allocated = Column(Boolean, default=False)
+ # leased means dhcp bridge has leased the ip
leased = Column(Boolean, default=False)
reserved = Column(Boolean, default=False)
+class FloatingIp(BASE, NovaBase):
+ """Represents a floating ip that dynamically forwards to a fixed ip."""
+ __tablename__ = 'floating_ips'
+ id = Column(Integer, primary_key=True)
+ address = Column(String(255))
+ fixed_ip_id = Column(Integer, ForeignKey('fixed_ips.id'), nullable=True)
+ fixed_ip = relationship(FixedIp,
+ backref=backref('floating_ips'),
+ foreign_keys=fixed_ip_id,
+ primaryjoin='and_('
+ 'FloatingIp.fixed_ip_id == FixedIp.id,'
+ 'FloatingIp.deleted == False)')
+ project_id = Column(String(255))
+ host = Column(String(255)) # , ForeignKey('hosts.id'))
+ auto_assigned = Column(Boolean, default=False, nullable=False)
+
+
+class AuthToken(BASE, NovaBase):
+ """Represents an authorization token for all API transactions.
+
+ Fields are a string representing the actual token and a user id for
+ mapping to the actual user
+
+ """
+ __tablename__ = 'auth_tokens'
+ token_hash = Column(String(255), primary_key=True)
+ user_id = Column(String(255))
+ server_management_url = Column(String(255))
+ storage_url = Column(String(255))
+ cdn_management_url = Column(String(255))
+
+
class User(BASE, NovaBase):
"""Represents a user."""
__tablename__ = 'users'
@@ -662,23 +697,6 @@ class UserProjectAssociation(BASE, NovaBase):
project_id = Column(String(255), ForeignKey(Project.id), primary_key=True)
-class FloatingIp(BASE, NovaBase):
- """Represents a floating ip that dynamically forwards to a fixed ip."""
- __tablename__ = 'floating_ips'
- id = Column(Integer, primary_key=True)
- address = Column(String(255))
- fixed_ip_id = Column(Integer, ForeignKey('fixed_ips.id'), nullable=True)
- fixed_ip = relationship(FixedIp,
- backref=backref('floating_ips'),
- foreign_keys=fixed_ip_id,
- primaryjoin='and_('
- 'FloatingIp.fixed_ip_id == FixedIp.id,'
- 'FloatingIp.deleted == False)')
- project_id = Column(String(255))
- host = Column(String(255)) # , ForeignKey('hosts.id'))
- auto_assigned = Column(Boolean, default=False, nullable=False)
-
-
class ConsolePool(BASE, NovaBase):
"""Represents pool of consoles on the same physical node."""
__tablename__ = 'console_pools'
@@ -718,6 +736,21 @@ class InstanceMetadata(BASE, NovaBase):
'InstanceMetadata.deleted == False)')
+class InstanceTypeExtraSpecs(BASE, NovaBase):
+ """Represents additional specs as key/value pairs for an instance_type"""
+ __tablename__ = 'instance_type_extra_specs'
+ id = Column(Integer, primary_key=True)
+ key = Column(String(255))
+ value = Column(String(255))
+ instance_type_id = Column(Integer, ForeignKey('instance_types.id'),
+ nullable=False)
+ instance_type = relationship(InstanceTypes, backref="extra_specs",
+ foreign_keys=instance_type_id,
+ primaryjoin='and_('
+ 'InstanceTypeExtraSpecs.instance_type_id == InstanceTypes.id,'
+ 'InstanceTypeExtraSpecs.deleted == False)')
+
+
class Zone(BASE, NovaBase):
"""Represents a child zone of this zone."""
__tablename__ = 'zones'
@@ -725,6 +758,8 @@ class Zone(BASE, NovaBase):
api_url = Column(String(255))
username = Column(String(255))
password = Column(String(255))
+ weight_offset = Column(Float(), default=0.0)
+ weight_scale = Column(Float(), default=1.0)
class AgentBuild(BASE, NovaBase):
@@ -752,7 +787,7 @@ def register_models():
Network, SecurityGroup, SecurityGroupIngressRule,
SecurityGroupInstanceAssociation, AuthToken, User,
Project, Certificate, ConsolePool, Console, Zone,
- AgentBuild, InstanceMetadata, Migration)
+ AgentBuild, InstanceMetadata, InstanceTypeExtraSpecs, Migration)
engine = create_engine(FLAGS.sql_connection, echo=False)
for model in models:
model.metadata.create_all(engine)