summaryrefslogtreecommitdiffstats
path: root/nova/db
diff options
context:
space:
mode:
authorMORITA Kazutaka <morita.kazutaka@gmail.com>2011-01-13 21:02:14 +0900
committerMORITA Kazutaka <morita.kazutaka@gmail.com>2011-01-13 21:02:14 +0900
commit40e13153d3a8ceef80fa40a59145672df796baa8 (patch)
tree9887fa6698a0cbae2da6211921df62308fbb1f79 /nova/db
parent1c694e9093c627bd50b35e9fb0ae11adf315a154 (diff)
parent621cf8e156582b3ff4dd44409324cc3a5f9aecf4 (diff)
downloadnova-40e13153d3a8ceef80fa40a59145672df796baa8.tar.gz
nova-40e13153d3a8ceef80fa40a59145672df796baa8.tar.xz
nova-40e13153d3a8ceef80fa40a59145672df796baa8.zip
Merge trunk
Diffstat (limited to 'nova/db')
-rw-r--r--nova/db/api.py86
-rw-r--r--nova/db/sqlalchemy/__init__.py23
-rw-r--r--nova/db/sqlalchemy/api.py251
-rw-r--r--nova/db/sqlalchemy/models.py58
-rw-r--r--nova/db/sqlalchemy/session.py4
5 files changed, 328 insertions, 94 deletions
diff --git a/nova/db/api.py b/nova/db/api.py
index 127f15478..1f81ef145 100644
--- a/nova/db/api.py
+++ b/nova/db/api.py
@@ -27,6 +27,9 @@ The underlying driver is loaded as a :class:`LazyPluggable`.
:sql_connection: string specifying the sqlalchemy connection to use, like:
`sqlite:///var/lib/nova/nova.sqlite`.
+
+:enable_new_services: when adding a new service to the database, is it in the
+ pool of available hardware (Default: True)
"""
from nova import exception
@@ -37,6 +40,8 @@ from nova import utils
FLAGS = flags.FLAGS
flags.DEFINE_string('db_backend', 'sqlalchemy',
'The backend to use for db')
+flags.DEFINE_boolean('enable_new_services', True,
+ 'Services to be added to the available pool on create')
IMPL = utils.LazyPluggable(FLAGS['db_backend'],
@@ -76,11 +81,21 @@ def service_get(context, service_id):
return IMPL.service_get(context, service_id)
+def service_get_all(context, disabled=False):
+ """Get all service."""
+ return IMPL.service_get_all(context, None, disabled)
+
+
def service_get_all_by_topic(context, topic):
- """Get all compute services for a given topic."""
+ """Get all services for a given topic."""
return IMPL.service_get_all_by_topic(context, topic)
+def service_get_all_by_host(context, host):
+ """Get all services for a given host."""
+ return IMPL.service_get_all_by_host(context, host)
+
+
def service_get_all_compute_sorted(context):
"""Get all compute services sorted by instance count.
@@ -348,9 +363,9 @@ def instance_get_project_vpn(context, project_id):
return IMPL.instance_get_project_vpn(context, project_id)
-def instance_get_by_internal_id(context, internal_id):
- """Get an instance by internal id."""
- return IMPL.instance_get_by_internal_id(context, internal_id)
+def instance_get_by_id(context, instance_id):
+ """Get an instance by id."""
+ return IMPL.instance_get_by_id(context, instance_id)
def instance_is_vpn(context, instance_id):
@@ -714,7 +729,7 @@ def security_group_get_all(context):
def security_group_get(context, security_group_id):
- """Get security group by its internal id."""
+ """Get security group by its id."""
return IMPL.security_group_get(context, security_group_id)
@@ -767,6 +782,13 @@ def security_group_rule_get_by_security_group(context, security_group_id):
security_group_id)
+def security_group_rule_get_by_security_group_grantee(context,
+ security_group_id):
+ """Get all rules that grant access to the given security group."""
+ return IMPL.security_group_rule_get_by_security_group_grantee(context,
+ security_group_id)
+
+
def security_group_rule_destroy(context, security_group_rule_id):
"""Deletes a security group rule."""
return IMPL.security_group_rule_destroy(context, security_group_rule_id)
@@ -889,3 +911,57 @@ def host_get_networks(context, host):
"""
return IMPL.host_get_networks(context, host)
+
+
+##################
+
+
+def console_pool_create(context, values):
+ """Create console pool."""
+ return IMPL.console_pool_create(context, values)
+
+
+def console_pool_get(context, pool_id):
+ """Get a console pool."""
+ return IMPL.console_pool_get(context, pool_id)
+
+
+def console_pool_get_by_host_type(context, compute_host, proxy_host,
+ console_type):
+ """Fetch a console pool for a given proxy host, compute host, and type."""
+ return IMPL.console_pool_get_by_host_type(context,
+ compute_host,
+ proxy_host,
+ console_type)
+
+
+def console_pool_get_all_by_host_type(context, host, console_type):
+ """Fetch all pools for given proxy host and type."""
+ return IMPL.console_pool_get_all_by_host_type(context,
+ host,
+ console_type)
+
+
+def console_create(context, values):
+ """Create a console."""
+ return IMPL.console_create(context, values)
+
+
+def console_delete(context, console_id):
+ """Delete a console."""
+ return IMPL.console_delete(context, console_id)
+
+
+def console_get_by_pool_instance(context, pool_id, instance_id):
+ """Get console entry for a given instance and pool."""
+ return IMPL.console_get_by_pool_instance(context, pool_id, instance_id)
+
+
+def console_get_all_by_instance(context, instance_id):
+ """Get consoles for a given instance."""
+ return IMPL.console_get_all_by_instance(context, instance_id)
+
+
+def console_get(context, console_id, instance_id=None):
+ """Get a specific console (possibly on a given instance)."""
+ return IMPL.console_get(context, console_id, instance_id)
diff --git a/nova/db/sqlalchemy/__init__.py b/nova/db/sqlalchemy/__init__.py
index 3288ebd20..501373942 100644
--- a/nova/db/sqlalchemy/__init__.py
+++ b/nova/db/sqlalchemy/__init__.py
@@ -19,6 +19,27 @@
"""
SQLAlchemy database backend
"""
+import time
+
+from sqlalchemy.exc import OperationalError
+
+from nova import flags
+from nova import log as logging
from nova.db.sqlalchemy import models
-models.register_models()
+
+FLAGS = flags.FLAGS
+LOG = logging.getLogger('nova.db.sqlalchemy')
+
+
+for i in xrange(FLAGS.sql_max_retries):
+ if i > 0:
+ time.sleep(FLAGS.sql_retry_interval)
+
+ try:
+ models.register_models()
+ break
+ except OperationalError:
+ LOG.exception(_("Data store %s is unreachable."
+ " Trying again in %d seconds."),
+ FLAGS.sql_connection, FLAGS.sql_retry_interval)
diff --git a/nova/db/sqlalchemy/api.py b/nova/db/sqlalchemy/api.py
index 8e68d12a4..39df21e30 100644
--- a/nova/db/sqlalchemy/api.py
+++ b/nova/db/sqlalchemy/api.py
@@ -19,7 +19,6 @@
Implementation of SQLAlchemy backend.
"""
-import random
import warnings
from nova import db
@@ -136,6 +135,18 @@ def service_get(context, service_id, session=None):
@require_admin_context
+def service_get_all(context, session=None, disabled=False):
+ if not session:
+ session = get_session()
+
+ result = session.query(models.Service).\
+ filter_by(deleted=can_read_deleted(context)).\
+ filter_by(disabled=disabled).\
+ all()
+ return result
+
+
+@require_admin_context
def service_get_all_by_topic(context, topic):
session = get_session()
return session.query(models.Service).\
@@ -146,6 +157,15 @@ def service_get_all_by_topic(context, topic):
@require_admin_context
+def service_get_all_by_host(context, host):
+ session = get_session()
+ return session.query(models.Service).\
+ filter_by(deleted=False).\
+ filter_by(host=host).\
+ all()
+
+
+@require_admin_context
def _service_get_all_topic_subquery(context, session, topic, subq, label):
sort_value = getattr(subq.c, label)
return session.query(models.Service, func.coalesce(sort_value, 0)).\
@@ -236,6 +256,8 @@ def service_get_by_args(context, host, binary):
def service_create(context, values):
service_ref = models.Service()
service_ref.update(values)
+ if not FLAGS.enable_new_services:
+ service_ref.disabled = True
service_ref.save()
return service_ref
@@ -604,30 +626,18 @@ def fixed_ip_update(context, address, values):
###################
-#TODO(gundlach): instance_create and volume_create are nearly identical
-#and should be refactored. I expect there are other copy-and-paste
-#functions between the two of them as well.
-
-
@require_context
def instance_create(context, values):
"""Create a new Instance record in the database.
context - request context object
values - dict containing column values.
- 'internal_id' is auto-generated and should not be specified.
"""
instance_ref = models.Instance()
instance_ref.update(values)
session = get_session()
with session.begin():
- while instance_ref.internal_id == None:
- # Instances have integer internal ids.
- internal_id = random.randint(0, 2 ** 31 - 1)
- if not instance_internal_id_exists(context, internal_id,
- session=session):
- instance_ref.internal_id = internal_id
instance_ref.save(session=session)
return instance_ref
@@ -661,7 +671,7 @@ def instance_get(context, instance_id, session=None):
if is_admin_context(context):
result = session.query(models.Instance).\
options(joinedload_all('fixed_ip.floating_ips')).\
- options(joinedload('security_groups')).\
+ options(joinedload_all('security_groups.rules')).\
options(joinedload('volumes')).\
filter_by(id=instance_id).\
filter_by(deleted=can_read_deleted(context)).\
@@ -669,7 +679,7 @@ def instance_get(context, instance_id, session=None):
elif is_user_context(context):
result = session.query(models.Instance).\
options(joinedload_all('fixed_ip.floating_ips')).\
- options(joinedload('security_groups')).\
+ options(joinedload_all('security_groups.rules')).\
options(joinedload('volumes')).\
filter_by(project_id=context.project_id).\
filter_by(id=instance_id).\
@@ -749,38 +759,31 @@ def instance_get_project_vpn(context, project_id):
@require_context
-def instance_get_by_internal_id(context, internal_id):
+def instance_get_by_id(context, instance_id):
session = get_session()
if is_admin_context(context):
result = session.query(models.Instance).\
options(joinedload('security_groups')).\
- filter_by(internal_id=internal_id).\
+ options(joinedload_all('fixed_ip.floating_ips')).\
+ filter_by(id=instance_id).\
filter_by(deleted=can_read_deleted(context)).\
first()
elif is_user_context(context):
result = session.query(models.Instance).\
options(joinedload('security_groups')).\
+ options(joinedload_all('fixed_ip.floating_ips')).\
filter_by(project_id=context.project_id).\
- filter_by(internal_id=internal_id).\
+ filter_by(id=instance_id).\
filter_by(deleted=False).\
first()
if not result:
- raise exception.NotFound(_('Instance %s not found') % (internal_id))
+ raise exception.NotFound(_('Instance %s not found') % (instance_id))
return result
@require_context
-def instance_internal_id_exists(context, internal_id, session=None):
- if not session:
- session = get_session()
- return session.query(exists().\
- where(models.Instance.internal_id == internal_id)).\
- one()[0]
-
-
-@require_context
def instance_get_fixed_address(context, instance_id):
session = get_session()
with session.begin():
@@ -860,12 +863,9 @@ def instance_action_create(context, values):
def instance_get_actions(context, instance_id):
"""Return the actions associated to the given instance id"""
session = get_session()
- actions = {}
- for action in session.query(models.InstanceActions).\
+ return session.query(models.InstanceActions).\
filter_by(instance_id=instance_id).\
- all():
- actions[action.action] = action.error
- return actions
+ all()
###################
@@ -1315,10 +1315,6 @@ def volume_create(context, values):
session = get_session()
with session.begin():
- while volume_ref.ec2_id == None:
- ec2_id = utils.generate_uid('vol')
- if not volume_ec2_id_exists(context, ec2_id, session=session):
- volume_ref.ec2_id = ec2_id
volume_ref.save(session=session)
return volume_ref
@@ -1416,41 +1412,6 @@ def volume_get_all_by_project(context, project_id):
all()
-@require_context
-def volume_get_by_ec2_id(context, ec2_id):
- session = get_session()
- result = None
-
- if is_admin_context(context):
- result = session.query(models.Volume).\
- filter_by(ec2_id=ec2_id).\
- filter_by(deleted=can_read_deleted(context)).\
- first()
- elif is_user_context(context):
- result = session.query(models.Volume).\
- filter_by(project_id=context.project_id).\
- filter_by(ec2_id=ec2_id).\
- filter_by(deleted=False).\
- first()
- else:
- raise exception.NotAuthorized()
-
- if not result:
- raise exception.NotFound(_('Volume %s not found') % ec2_id)
-
- return result
-
-
-@require_context
-def volume_ec2_id_exists(context, ec2_id, session=None):
- if not session:
- session = get_session()
-
- return session.query(exists().\
- where(models.Volume.id == ec2_id)).\
- one()[0]
-
-
@require_admin_context
def volume_get_instance(context, volume_id):
session = get_session()
@@ -1641,6 +1602,44 @@ def security_group_rule_get(context, security_group_rule_id, session=None):
@require_context
+def security_group_rule_get_by_security_group(context, security_group_id,
+ session=None):
+ if not session:
+ session = get_session()
+ if is_admin_context(context):
+ result = session.query(models.SecurityGroupIngressRule).\
+ filter_by(deleted=can_read_deleted(context)).\
+ filter_by(parent_group_id=security_group_id).\
+ all()
+ else:
+ # TODO(vish): Join to group and check for project_id
+ result = session.query(models.SecurityGroupIngressRule).\
+ filter_by(deleted=False).\
+ filter_by(parent_group_id=security_group_id).\
+ all()
+ return result
+
+
+@require_context
+def security_group_rule_get_by_security_group_grantee(context,
+ security_group_id,
+ session=None):
+ if not session:
+ session = get_session()
+ if is_admin_context(context):
+ result = session.query(models.SecurityGroupIngressRule).\
+ filter_by(deleted=can_read_deleted(context)).\
+ filter_by(group_id=security_group_id).\
+ all()
+ else:
+ result = session.query(models.SecurityGroupIngressRule).\
+ filter_by(deleted=False).\
+ filter_by(group_id=security_group_id).\
+ all()
+ return result
+
+
+@require_context
def security_group_rule_create(context, values):
security_group_rule_ref = models.SecurityGroupIngressRule()
security_group_rule_ref.update(values)
@@ -1875,3 +1874,111 @@ def host_get_networks(context, host):
filter_by(deleted=False).\
filter_by(host=host).\
all()
+
+
+##################
+
+
+def console_pool_create(context, values):
+ pool = models.ConsolePool()
+ pool.update(values)
+ pool.save()
+ return pool
+
+
+def console_pool_get(context, pool_id):
+ session = get_session()
+ result = session.query(models.ConsolePool).\
+ filter_by(deleted=False).\
+ filter_by(id=pool_id).\
+ first()
+ if not result:
+ raise exception.NotFound(_("No console pool with id %(pool_id)s") %
+ {'pool_id': pool_id})
+
+ return result
+
+
+def console_pool_get_by_host_type(context, compute_host, host,
+ console_type):
+ session = get_session()
+ result = session.query(models.ConsolePool).\
+ filter_by(host=host).\
+ filter_by(console_type=console_type).\
+ filter_by(compute_host=compute_host).\
+ filter_by(deleted=False).\
+ options(joinedload('consoles')).\
+ first()
+ if not result:
+ raise exception.NotFound(_('No console pool of type %(type)s '
+ 'for compute host %(compute_host)s '
+ 'on proxy host %(host)s') %
+ {'type': console_type,
+ 'compute_host': compute_host,
+ 'host': host})
+ return result
+
+
+def console_pool_get_all_by_host_type(context, host, console_type):
+ session = get_session()
+ return session.query(models.ConsolePool).\
+ filter_by(host=host).\
+ filter_by(console_type=console_type).\
+ filter_by(deleted=False).\
+ options(joinedload('consoles')).\
+ all()
+
+
+def console_create(context, values):
+ console = models.Console()
+ console.update(values)
+ console.save()
+ return console
+
+
+def console_delete(context, console_id):
+ session = get_session()
+ with session.begin():
+ # consoles are meant to be transient. (mdragon)
+ session.execute('delete from consoles '
+ 'where id=:id', {'id': console_id})
+
+
+def console_get_by_pool_instance(context, pool_id, instance_id):
+ session = get_session()
+ result = session.query(models.Console).\
+ filter_by(pool_id=pool_id).\
+ filter_by(instance_id=instance_id).\
+ options(joinedload('pool')).\
+ first()
+ if not result:
+ raise exception.NotFound(_('No console for instance %(instance_id)s '
+ 'in pool %(pool_id)s') %
+ {'instance_id': instance_id,
+ 'pool_id': pool_id})
+ return result
+
+
+def console_get_all_by_instance(context, instance_id):
+ session = get_session()
+ results = session.query(models.Console).\
+ filter_by(instance_id=instance_id).\
+ options(joinedload('pool')).\
+ all()
+ return results
+
+
+def console_get(context, console_id, instance_id=None):
+ session = get_session()
+ query = session.query(models.Console).\
+ filter_by(id=console_id)
+ if instance_id:
+ query = query.filter_by(instance_id=instance_id)
+ result = query.options(joinedload('pool')).first()
+ if not result:
+ idesc = (_("on instance %s") % instance_id) if instance_id else ""
+ raise exception.NotFound(_("No console with id %(console_id)s"
+ " %(instance)s") %
+ {'instance': idesc,
+ 'console_id': console_id})
+ return result
diff --git a/nova/db/sqlalchemy/models.py b/nova/db/sqlalchemy/models.py
index a050aef23..1dc46fe78 100644
--- a/nova/db/sqlalchemy/models.py
+++ b/nova/db/sqlalchemy/models.py
@@ -149,6 +149,7 @@ class Service(BASE, NovaBase):
topic = Column(String(255))
report_count = Column(Integer, nullable=False, default=0)
disabled = Column(Boolean, default=False)
+ availability_zone = Column(String(255), default='nova')
class Certificate(BASE, NovaBase):
@@ -164,11 +165,13 @@ class Certificate(BASE, NovaBase):
class Instance(BASE, NovaBase):
"""Represents a guest vm."""
__tablename__ = 'instances'
- id = Column(Integer, primary_key=True)
- internal_id = Column(Integer, unique=True)
+ id = Column(Integer, primary_key=True, autoincrement=True)
- admin_pass = Column(String(255))
+ @property
+ def name(self):
+ return "instance-%08x" % self.id
+ admin_pass = Column(String(255))
user_id = Column(String(255))
project_id = Column(String(255))
@@ -180,10 +183,6 @@ class Instance(BASE, NovaBase):
def project(self):
return auth.manager.AuthManager().get_project(self.project_id)
- @property
- def name(self):
- return "instance-%d" % self.internal_id
-
image_id = Column(String(255))
kernel_id = Column(String(255))
ramdisk_id = Column(String(255))
@@ -220,10 +219,14 @@ class Instance(BASE, NovaBase):
launched_at = Column(DateTime)
terminated_at = Column(DateTime)
+ availability_zone = Column(String(255))
+
# User editable field for display in user-facing UIs
display_name = Column(String(255))
display_description = Column(String(255))
+ locked = Column(Boolean)
+
# TODO(vish): see Ewan's email about state improvements, probably
# should be in a driver base class or some such
# vmstate_state = running, halted, suspended, paused
@@ -249,8 +252,11 @@ class InstanceActions(BASE, NovaBase):
class Volume(BASE, NovaBase):
"""Represents a block storage device that can be attached to a vm."""
__tablename__ = 'volumes'
- id = Column(Integer, primary_key=True)
- ec2_id = Column(String(12), unique=True)
+ id = Column(Integer, primary_key=True, autoincrement=True)
+
+ @property
+ def name(self):
+ return "volume-%08x" % self.id
user_id = Column(String(255))
project_id = Column(String(255))
@@ -276,10 +282,6 @@ class Volume(BASE, NovaBase):
display_name = Column(String(255))
display_description = Column(String(255))
- @property
- def name(self):
- return self.ec2_id
-
class Quota(BASE, NovaBase):
"""Represents quota overrides for a project."""
@@ -539,18 +541,44 @@ class FloatingIp(BASE, NovaBase):
host = Column(String(255)) # , ForeignKey('hosts.id'))
+class ConsolePool(BASE, NovaBase):
+ """Represents pool of consoles on the same physical node."""
+ __tablename__ = 'console_pools'
+ id = Column(Integer, primary_key=True)
+ address = Column(String(255))
+ username = Column(String(255))
+ password = Column(String(255))
+ console_type = Column(String(255))
+ public_hostname = Column(String(255))
+ host = Column(String(255))
+ compute_host = Column(String(255))
+
+
+class Console(BASE, NovaBase):
+ """Represents a console session for an instance."""
+ __tablename__ = 'consoles'
+ id = Column(Integer, primary_key=True)
+ instance_name = Column(String(255))
+ instance_id = Column(Integer)
+ password = Column(String(255))
+ port = Column(Integer, nullable=True)
+ pool_id = Column(Integer, ForeignKey('console_pools.id'))
+ pool = relationship(ConsolePool, backref=backref('consoles'))
+
+
def register_models():
"""Register Models and create metadata.
Called from nova.db.sqlalchemy.__init__ as part of loading the driver,
- it will never need to be called explicitly elsewhere.
+ it will never need to be called explicitly elsewhere unless the
+ connection is lost and needs to be reestablished.
"""
from sqlalchemy import create_engine
models = (Service, Instance, InstanceActions,
Volume, ExportDevice, IscsiTarget, FixedIp, FloatingIp,
Network, SecurityGroup, SecurityGroupIngressRule,
SecurityGroupInstanceAssociation, AuthToken, User,
- Project, Certificate) # , Image, Host
+ Project, Certificate, ConsolePool, Console) # , Image, Host
engine = create_engine(FLAGS.sql_connection, echo=False)
for model in models:
model.metadata.create_all(engine)
diff --git a/nova/db/sqlalchemy/session.py b/nova/db/sqlalchemy/session.py
index e0d84c107..c3876c02a 100644
--- a/nova/db/sqlalchemy/session.py
+++ b/nova/db/sqlalchemy/session.py
@@ -36,7 +36,9 @@ def get_session(autocommit=True, expire_on_commit=False):
global _MAKER
if not _MAKER:
if not _ENGINE:
- _ENGINE = create_engine(FLAGS.sql_connection, echo=False)
+ _ENGINE = create_engine(FLAGS.sql_connection,
+ pool_recycle=FLAGS.sql_idle_timeout,
+ echo=False)
_MAKER = (sessionmaker(bind=_ENGINE,
autocommit=autocommit,
expire_on_commit=expire_on_commit))