Refactoring the instance load method Adding back the volume used parameter. Added timeouts for all guest sync calls. Refactored the instance models.

This commit is contained in:
Nirmal Ranganathan 2012-06-17 11:01:14 -05:00
parent 3380bbb738
commit c7d93e7ce7
25 changed files with 383 additions and 200 deletions

View File

@ -74,7 +74,8 @@ class Commands(object):
def image_update(self, service_name, image_id): def image_update(self, service_name, image_id):
db_api.configure_db(self.conf) db_api.configure_db(self.conf)
image = db_api.find_by(instance_models.ServiceImage, service_name=service_name) image = db_api.find_by(instance_models.ServiceImage,
service_name=service_name)
if image is None: if image is None:
# Create a new one # Create a new one
image = instance_models.ServiceImage() image = instance_models.ServiceImage()

View File

@ -33,6 +33,9 @@ nova_volume_url = http://localhost:8776/v1
# Config options for enabling volume service # Config options for enabling volume service
reddwarf_volume_support = True reddwarf_volume_support = True
block_device_mapping = /var/lib/mysql
device_path = /var/lib/mysql
mount_point = /var/lib/mysql
volume_time_out=30 volume_time_out=30
# Configuration options for talking to nova via the novaclient. # Configuration options for talking to nova via the novaclient.
@ -50,6 +53,11 @@ taskmanager_manager=reddwarf.taskmanager.manager.TaskManager
# Reddwarf DNS # Reddwarf DNS
reddwarf_dns_support = False reddwarf_dns_support = False
# Guest related conf
agent_heartbeat_time = 10
agent_call_low_timeout = 5
agent_call_high_timeout = 100
# ============ notifer queue kombu connection options ======================== # ============ notifer queue kombu connection options ========================
notifier_queue_hostname = localhost notifier_queue_hostname = localhost

View File

@ -47,7 +47,7 @@ add_addresses = True
# Config options for enabling volume service # Config options for enabling volume service
reddwarf_volume_support = True reddwarf_volume_support = True
block_device_mapping = /var/lib/mysql block_device_mapping = /var/lib/mysql
device_path = /dev/vdb device_path = /var/lib/mysql
mount_point = /var/lib/mysql mount_point = /var/lib/mysql
max_accepted_volume_size = 10 max_accepted_volume_size = 10
volume_time_out=30 volume_time_out=30
@ -58,6 +58,11 @@ reddwarf_dns_support = False
# Auth # Auth
admin_roles = [admin] admin_roles = [admin]
# Guest related conf
agent_heartbeat_time = 10
agent_call_low_timeout = 5
agent_call_high_timeout = 100
# ============ notifer queue kombu connection options ======================== # ============ notifer queue kombu connection options ========================
notifier_queue_hostname = localhost notifier_queue_hostname = localhost

View File

@ -64,6 +64,14 @@ mount_point = /var/lib/mysql
max_accepted_volume_size = 10 max_accepted_volume_size = 10
volume_time_out=30 volume_time_out=30
# Auth
admin_roles = [admin]
# Guest related conf
agent_heartbeat_time = 10
agent_call_low_timeout = 5
agent_call_high_timeout = 100
# ============ notifer queue kombu connection options ======================== # ============ notifer queue kombu connection options ========================
notifier_queue_hostname = localhost notifier_queue_hostname = localhost

View File

@ -88,6 +88,11 @@ class GuestError(ReddwarfError):
"%(original_message)s.") "%(original_message)s.")
class GuestTimeout(ReddwarfError):
message = _("Timeout trying to connect to the Guest Agent.")
class BadRequest(ReddwarfError): class BadRequest(ReddwarfError):
message = _("The server could not comply with the request since it is " message = _("The server could not comply with the request since it is "
@ -147,3 +152,13 @@ class PollTimeOut(ReddwarfError):
class Forbidden(ReddwarfError): class Forbidden(ReddwarfError):
message = _("User does not have admin privileges.") message = _("User does not have admin privileges.")
class InvalidModelError(ReddwarfError):
message = _("The following values are invalid: %(errors)s")
class ModelNotFoundError(NotFound):
message = _("Not Found")

View File

@ -46,14 +46,15 @@ eventlet.patcher.monkey_patch(all=False, socket=True)
LOG = logging.getLogger('reddwarf.common.wsgi') LOG = logging.getLogger('reddwarf.common.wsgi')
XMLNS = 'http://docs.openstack.org/database/api/v1.0' XMLNS = 'http://docs.openstack.org/database/api/v1.0'
CUSTOM_PLURALS_METADATA = {'databases':'', 'users':''} CUSTOM_PLURALS_METADATA = {'databases': '', 'users': ''}
CUSTOM_SERIALIZER_METADATA = {'instance': {'status':'', 'hostname':'', CUSTOM_SERIALIZER_METADATA = {'instance': {'status': '', 'hostname': '',
'id':'', 'name':'','created':'', 'updated':''}, 'id': '', 'name': '', 'created': '',
'volume': {'size':'', 'used':''}, 'updated': ''},
'flavor': {'id':'', 'ram': '', 'name': ''}, 'volume': {'size': '', 'used': ''},
'link': {'href':'', 'rel': ''}, 'flavor': {'id': '', 'ram': '', 'name': ''},
'database': {'name':''}, 'link': {'href': '', 'rel': ''},
'user': {'name':'', 'password':''}} 'database': {'name': ''},
'user': {'name': '', 'password': ''}}
def versioned_urlmap(*args, **kwargs): def versioned_urlmap(*args, **kwargs):

92
reddwarf/db/models.py Normal file
View File

@ -0,0 +1,92 @@
# 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.
import logging
from reddwarf import db
from reddwarf.common import exception
from reddwarf.common import models
from reddwarf.common import pagination
from reddwarf.common import utils
LOG = logging.getLogger(__name__)
class DatabaseModelBase(models.ModelBase):
_auto_generated_attrs = ['id']
@classmethod
def create(cls, **values):
values['id'] = utils.generate_uuid()
values['created'] = utils.utcnow()
instance = cls(**values).save()
if not instance.is_valid():
raise exception.InvalidModelError(errors=instance.errors)
return instance
def save(self):
if not self.is_valid():
raise exception.InvalidModelError(errors=self.errors)
self['updated'] = utils.utcnow()
LOG.debug(_("Saving %s: %s") %
(self.__class__.__name__, self.__dict__))
return db.db_api.save(self)
def delete(self):
self['updated'] = utils.utcnow()
LOG.debug(_("Deleting %s: %s") %
(self.__class__.__name__, self.__dict__))
return db.db_api.delete(self)
def __init__(self, **kwargs):
self.merge_attributes(kwargs)
if not self.is_valid():
raise exception.InvalidModelError(errors=self.errors)
def merge_attributes(self, values):
"""dict.update() behaviour."""
for k, v in values.iteritems():
self[k] = v
@classmethod
def find_by(cls, **conditions):
model = cls.get_by(**conditions)
if model is None:
raise exception.ModelNotFoundError(_("%s Not Found") %
cls.__name__)
return model
@classmethod
def get_by(cls, **kwargs):
return db.db_api.find_by(cls, **cls._process_conditions(kwargs))
@classmethod
def find_all(cls, **kwargs):
return db.db_query.find_all(cls, **cls._process_conditions(kwargs))
@classmethod
def _process_conditions(cls, raw_conditions):
"""Override in inheritors to format/modify any conditions."""
return raw_conditions
@classmethod
def find_by_pagination(cls, collection_type, collection_query,
paginated_url, **kwargs):
elements, next_marker = collection_query.paginated_collection(**kwargs)
return pagination.PaginatedDataView(collection_type,
elements,
paginated_url,
next_marker)

View File

@ -36,6 +36,8 @@ def map(engine, models):
Table('service_statuses', meta, autoload=True)) Table('service_statuses', meta, autoload=True))
orm.mapper(models['dns_records'], orm.mapper(models['dns_records'],
Table('dns_records', meta, autoload=True)) Table('dns_records', meta, autoload=True))
orm.mapper(models['agent_heartbeats'],
Table('agent_heartbeats', meta, autoload=True))
def mapping_exists(model): def mapping_exists(model):

View File

@ -42,11 +42,13 @@ def configure_db(options, models_mapper=None):
from reddwarf.instance import models as base_models from reddwarf.instance import models as base_models
from reddwarf.dns import models as dns_models from reddwarf.dns import models as dns_models
from reddwarf.extensions.mysql import models as mysql_models from reddwarf.extensions.mysql import models as mysql_models
from reddwarf.guestagent import models as agent_models
model_modules = [ model_modules = [
base_models, base_models,
dns_models, dns_models,
mysql_models, mysql_models,
agent_models,
] ]
models = {} models = {}

View File

@ -22,9 +22,8 @@ Model classes that map instance Ip to dns record.
import logging import logging
from reddwarf import db from reddwarf import db
from reddwarf.common import exception
from reddwarf.common.models import ModelBase from reddwarf.common.models import ModelBase
from reddwarf.instance.models import InvalidModelError
from reddwarf.instance.models import ModelNotFoundError
LOG = logging.getLogger(__name__) LOG = logging.getLogger(__name__)
@ -48,12 +47,12 @@ class DnsRecord(ModelBase):
def create(cls, **values): def create(cls, **values):
record = cls(**values).save() record = cls(**values).save()
if not record.is_valid(): if not record.is_valid():
raise InvalidModelError(record.errors) raise exception.InvalidModelError(errors=record.errors)
return record return record
def save(self): def save(self):
if not self.is_valid(): if not self.is_valid():
raise InvalidModelError(self.errors) raise exception.InvalidModelError(errors=self.errors)
LOG.debug(_("Saving %s: %s") % LOG.debug(_("Saving %s: %s") %
(self.__class__.__name__, self.__dict__)) (self.__class__.__name__, self.__dict__))
return db.db_api.save(self) return db.db_api.save(self)
@ -67,7 +66,8 @@ class DnsRecord(ModelBase):
def find_by(cls, **conditions): def find_by(cls, **conditions):
model = cls.get_by(**conditions) model = cls.get_by(**conditions)
if model is None: if model is None:
raise ModelNotFoundError(_("%s Not Found") % cls.__name__) raise exception.ModelNotFoundError(_("%s Not Found") %
cls.__name__)
return model return model
@classmethod @classmethod

View File

@ -218,6 +218,3 @@ class RsDnsZone(object):
def __str__(self): def __str__(self):
return "%s:%s" % (self.id, self.name) return "%s:%s" % (self.id, self.name)

View File

@ -22,7 +22,6 @@ from reddwarf.common import exception
from reddwarf.common import pagination from reddwarf.common import pagination
from reddwarf.common import wsgi from reddwarf.common import wsgi
from reddwarf.guestagent.db import models as guest_models from reddwarf.guestagent.db import models as guest_models
from reddwarf.instance import models as instance_models
from reddwarf.extensions.mysql import models from reddwarf.extensions.mysql import models
from reddwarf.extensions.mysql import views from reddwarf.extensions.mysql import views
@ -44,7 +43,7 @@ class BaseController(wsgi.Controller):
], ],
webob.exc.HTTPNotFound: [ webob.exc.HTTPNotFound: [
exception.NotFound, exception.NotFound,
instance_models.ModelNotFoundError, exception.ModelNotFoundError,
], ],
webob.exc.HTTPConflict: [ webob.exc.HTTPConflict: [
], ],

View File

@ -20,7 +20,7 @@
from reddwarf import db from reddwarf import db
from novaclient import exceptions as nova_exceptions from novaclient import exceptions as nova_exceptions
from reddwarf.common import exception as rd_exceptions from reddwarf.common import exception
from reddwarf.common import utils from reddwarf.common import utils
from reddwarf.common.models import NovaRemoteModelBase from reddwarf.common.models import NovaRemoteModelBase
from reddwarf.common.remote import create_nova_client from reddwarf.common.remote import create_nova_client
@ -39,13 +39,13 @@ class Flavor(object):
client = create_nova_client(context) client = create_nova_client(context)
self.flavor = client.flavors.get(flavor_id) self.flavor = client.flavors.get(flavor_id)
except nova_exceptions.NotFound, e: except nova_exceptions.NotFound, e:
raise rd_exceptions.NotFound(uuid=flavor_id) raise exception.NotFound(uuid=flavor_id)
except nova_exceptions.ClientException, e: except nova_exceptions.ClientException, e:
raise rd_exceptions.ReddwarfError(str(e)) raise exception.ReddwarfError(str(e))
return return
msg = ("Flavor is not defined, and" msg = ("Flavor is not defined, and"
" context and flavor_id were not specified.") " context and flavor_id were not specified.")
raise InvalidModelError(msg) raise exception.InvalidModelError(errors=msg)
@property @property
def id(self): def id(self):

View File

@ -19,16 +19,19 @@
Handles all request to the Platform or Guest VM Handles all request to the Platform or Guest VM
""" """
import logging import logging
from eventlet import Timeout
from reddwarf import rpc from reddwarf import rpc
from reddwarf.common import config from reddwarf.common import config
from reddwarf.common import exception from reddwarf.common import exception
from reddwarf.common import utils from reddwarf.common import utils
# from nova.db import api as dbapi
LOG = logging.getLogger(__name__) LOG = logging.getLogger(__name__)
AGENT_LOW_TIMEOUT = int(config.Config.get('agent_call_low_timeout', 5))
AGENT_HIGH_TIMEOUT = int(config.Config.get('agent_call_high_timeout', 60))
class API(object): class API(object):
@ -38,22 +41,30 @@ class API(object):
self.context = context self.context = context
self.id = id self.id = id
def _call(self, method_name, **kwargs): def _call(self, method_name, timeout_sec, **kwargs):
LOG.debug("Calling %s" % method_name) LOG.debug("Calling %s" % method_name)
timeout = Timeout(timeout_sec)
try: try:
result = rpc.call(self.context, self._get_routing_key(), result = rpc.call(self.context, self._get_routing_key(),
{"method": method_name, "args": kwargs}) {'method': method_name, 'args': kwargs})
LOG.debug("Result is %s" % result) LOG.debug("Result is %s" % result)
return result return result
except Exception as e: except Exception as e:
LOG.error(e) LOG.error(e)
raise exception.GuestError(original_message=str(e)) raise exception.GuestError(original_message=str(e))
except Timeout as t:
if t is not timeout:
raise
else:
raise exception.GuestTimeout()
finally:
timeout.cancel()
def _cast(self, method_name, **kwargs): def _cast(self, method_name, **kwargs):
try: try:
rpc.cast(self.context, self._get_routing_key(), rpc.cast(self.context, self._get_routing_key(),
{"method": method_name, {'method': method_name, 'args': kwargs})
"args": kwargs})
except Exception as e: except Exception as e:
LOG.error(e) LOG.error(e)
raise exception.GuestError(original_message=str(e)) raise exception.GuestError(original_message=str(e))
@ -61,7 +72,7 @@ class API(object):
def _cast_with_consumer(self, method_name, **kwargs): def _cast_with_consumer(self, method_name, **kwargs):
try: try:
rpc.cast_with_consumer(self.context, self._get_routing_key(), rpc.cast_with_consumer(self.context, self._get_routing_key(),
{"method": method_name, "args": kwargs}) {'method': method_name, 'args': kwargs})
except Exception as e: except Exception as e:
LOG.error(e) LOG.error(e)
raise exception.GuestError(original_message=str(e)) raise exception.GuestError(original_message=str(e))
@ -78,8 +89,8 @@ class API(object):
def list_users(self, limit=None, marker=None, include_marker=False): def list_users(self, limit=None, marker=None, include_marker=False):
"""Make an asynchronous call to list database users""" """Make an asynchronous call to list database users"""
LOG.debug(_("Listing Users for Instance %s"), self.id) LOG.debug(_("Listing Users for Instance %s"), self.id)
return self._call("list_users", limit=limit, marker=marker, return self._call("list_users", AGENT_LOW_TIMEOUT, limit=limit,
include_marker=include_marker) marker=marker, include_marker=include_marker)
def delete_user(self, user): def delete_user(self, user):
"""Make an asynchronous call to delete an existing database user""" """Make an asynchronous call to delete an existing database user"""
@ -95,8 +106,8 @@ class API(object):
def list_databases(self, limit=None, marker=None, include_marker=False): def list_databases(self, limit=None, marker=None, include_marker=False):
"""Make an asynchronous call to list databases""" """Make an asynchronous call to list databases"""
LOG.debug(_("Listing databases for Instance %s"), self.id) LOG.debug(_("Listing databases for Instance %s"), self.id)
return self._call("list_databases", limit=limit, marker=marker, return self._call("list_databases", AGENT_LOW_TIMEOUT, limit=limit,
include_marker=include_marker) marker=marker, include_marker=include_marker)
def delete_database(self, database): def delete_database(self, database):
"""Make an asynchronous call to delete an existing database """Make an asynchronous call to delete an existing database
@ -108,24 +119,24 @@ class API(object):
"""Make a synchronous call to enable the root user for """Make a synchronous call to enable the root user for
access from anywhere""" access from anywhere"""
LOG.debug(_("Enable root user for Instance %s"), self.id) LOG.debug(_("Enable root user for Instance %s"), self.id)
return self._call("enable_root") return self._call("enable_root", AGENT_LOW_TIMEOUT)
def disable_root(self): def disable_root(self):
"""Make a synchronous call to disable the root user for """Make a synchronous call to disable the root user for
access from anywhere""" access from anywhere"""
LOG.debug(_("Disable root user for Instance %s"), self.id) LOG.debug(_("Disable root user for Instance %s"), self.id)
return self._call("disable_root") return self._call("disable_root", AGENT_LOW_TIMEOUT)
def is_root_enabled(self): def is_root_enabled(self):
"""Make a synchronous call to check if root access is """Make a synchronous call to check if root access is
available for the container""" available for the container"""
LOG.debug(_("Check root access for Instance %s"), self.id) LOG.debug(_("Check root access for Instance %s"), self.id)
return self._call("is_root_enabled") return self._call("is_root_enabled", AGENT_LOW_TIMEOUT)
def get_diagnostics(self): def get_diagnostics(self):
"""Make a synchronous call to get diagnostics for the container""" """Make a synchronous call to get diagnostics for the container"""
LOG.debug(_("Check diagnostics on Instance %s"), self.id) LOG.debug(_("Check diagnostics on Instance %s"), self.id)
return self._call("get_diagnostics") return self._call("get_diagnostics", AGENT_LOW_TIMEOUT)
def prepare(self, memory_mb, databases, users, def prepare(self, memory_mb, databases, users,
device_path='/dev/vdb', mount_point='/mnt/volume'): device_path='/dev/vdb', mount_point='/mnt/volume'):
@ -139,20 +150,26 @@ class API(object):
def restart(self): def restart(self):
"""Restart the MySQL server.""" """Restart the MySQL server."""
LOG.debug(_("Sending the call to restart MySQL on the Guest.")) LOG.debug(_("Sending the call to restart MySQL on the Guest."))
self._call("restart") self._call("restart", AGENT_HIGH_TIMEOUT)
def start_mysql_with_conf_changes(self, updated_memory_size): def start_mysql_with_conf_changes(self, updated_memory_size):
"""Start the MySQL server.""" """Start the MySQL server."""
LOG.debug(_("Sending the call to start MySQL on the Guest.")) LOG.debug(_("Sending the call to start MySQL on the Guest."))
self._call("start_mysql_with_conf_changes", self._call("start_mysql_with_conf_changes", AGENT_HIGH_TIMEOUT,
updated_memory_size=updated_memory_size) updated_memory_size=updated_memory_size)
def stop_mysql(self): def stop_mysql(self):
"""Stop the MySQL server.""" """Stop the MySQL server."""
LOG.debug(_("Sending the call to stop MySQL on the Guest.")) LOG.debug(_("Sending the call to stop MySQL on the Guest."))
self._call("stop_mysql") self._call("stop_mysql", AGENT_HIGH_TIMEOUT)
def upgrade(self): def upgrade(self):
"""Make an asynchronous call to self upgrade the guest agent""" """Make an asynchronous call to self upgrade the guest agent"""
LOG.debug(_("Sending an upgrade call to nova-guest")) LOG.debug(_("Sending an upgrade call to nova-guest"))
self._cast_with_consumer("upgrade") self._cast_with_consumer("upgrade")
def get_volume_info(self):
"""Make a synchronous call to get volume info for the container"""
LOG.debug(_("Check Volume Info on Instance %s"), self.id)
return self._call("get_filesystem_stats", AGENT_LOW_TIMEOUT,
fs_path="/var/lib/mysql")

View File

@ -72,6 +72,7 @@ INCLUDE_MARKER_OPERATORS = {
False: ">" False: ">"
} }
def generate_random_password(): def generate_random_password():
return str(uuid.uuid4()) return str(uuid.uuid4())

View File

@ -0,0 +1,65 @@
# 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.
import logging
from datetime import datetime
from datetime import timedelta
from reddwarf import db
from reddwarf.common import config
from reddwarf.common import exception
from reddwarf.common import utils
from reddwarf.db import models as dbmodels
LOG = logging.getLogger(__name__)
AGENT_HEARTBEAT = int(config.Config.get('agent_heartbeat_time', '10'))
def persisted_models():
return {
'agent_heartbeats': AgentHeartBeat,
}
class AgentHeartBeat(dbmodels.DatabaseModelBase):
"""Defines the state of a Guest Agent."""
_data_fields = ['instance_id', 'updated_at']
_table_name = 'agent_heartbeats'
def __init__(self, **kwargs):
super(AgentHeartBeat, self).__init__(**kwargs)
@classmethod
def create(cls, **values):
values['id'] = utils.generate_uuid()
heartbeat = cls(**values).save()
if not heartbeat.is_valid():
raise exception.InvalidModelError(errors=heartbeat.errors)
return heartbeat
def save(self):
if not self.is_valid():
raise exception.InvalidModelError(errors=self.errors)
self['updated_at'] = utils.utcnow()
LOG.debug(_("Saving %s: %s") %
(self.__class__.__name__, self.__dict__))
return db.db_api.save(self)
@staticmethod
def is_active(agent):
return (datetime.now() - agent.updated_at) < \
timedelta(seconds=AGENT_HEARTBEAT)

View File

@ -24,7 +24,8 @@ Intermediary class for building SQL queries for use by the guest agent.
class Query(object): class Query(object):
def __init__(self, columns=None, tables=None, where=None, order=None, group=None, limit=None): def __init__(self, columns=None, tables=None, where=None, order=None,
group=None, limit=None):
self.columns = columns or [] self.columns = columns or []
self.tables = tables or [] self.tables = tables or []
self.where = where or [] self.where = where or []

View File

@ -21,27 +21,21 @@ import eventlet
import logging import logging
import netaddr import netaddr
from reddwarf import db
from novaclient import exceptions as nova_exceptions from novaclient import exceptions as nova_exceptions
from reddwarf.common import config from reddwarf.common import config
from reddwarf.common import exception as rd_exceptions from reddwarf.common import exception
from reddwarf.common import pagination
from reddwarf.common import utils
from reddwarf.common.models import ModelBase
from novaclient import exceptions as nova_exceptions
from reddwarf.common.remote import create_dns_client from reddwarf.common.remote import create_dns_client
from reddwarf.common.remote import create_guest_client from reddwarf.common.remote import create_guest_client
from reddwarf.common.remote import create_nova_client from reddwarf.common.remote import create_nova_client
from reddwarf.common.remote import create_nova_volume_client from reddwarf.common.remote import create_nova_volume_client
from reddwarf.common.utils import poll_until from reddwarf.db import models as dbmodels
from reddwarf.instance.tasks import InstanceTask from reddwarf.instance.tasks import InstanceTask
from reddwarf.instance.tasks import InstanceTasks from reddwarf.instance.tasks import InstanceTasks
from reddwarf.guestagent import models as agent_models
from reddwarf.taskmanager import api as task_api from reddwarf.taskmanager import api as task_api
from eventlet import greenthread from eventlet import greenthread
from reddwarf.instance.views import get_ip_address
CONFIG = config.Config CONFIG = config.Config
@ -55,10 +49,10 @@ def load_server(context, instance_id, server_id):
server = client.servers.get(server_id) server = client.servers.get(server_id)
except nova_exceptions.NotFound, e: except nova_exceptions.NotFound, e:
LOG.debug("Could not find nova server_id(%s)" % server_id) LOG.debug("Could not find nova server_id(%s)" % server_id)
raise rd_exceptions.ComputeInstanceNotFound(instance_id=instance_id, raise exception.ComputeInstanceNotFound(instance_id=instance_id,
server_id=server_id) server_id=server_id)
except nova_exceptions.ClientException, e: except nova_exceptions.ClientException, e:
raise rd_exceptions.ReddwarfError(str(e)) raise exception.ReddwarfError(str(e))
return server return server
@ -80,7 +74,7 @@ def populate_databases(dbs):
databases.append(mydb.serialize()) databases.append(mydb.serialize())
return databases return databases
except ValueError as ve: except ValueError as ve:
raise rd_exceptions.BadRequest(ve.message) raise exception.BadRequest(ve.message)
class InstanceStatus(object): class InstanceStatus(object):
@ -114,7 +108,7 @@ def load_simple_instance_server_status(context, db_info):
# then assume the delete operation is done and raise an # then assume the delete operation is done and raise an
# exception. # exception.
if InstanceTasks.DELETING == db_info.task_status: if InstanceTasks.DELETING == db_info.task_status:
raise rd_exceptions.NotFound(uuid=db_info.id) raise exception.NotFound(uuid=db_info.id)
# If the compute server is in any of these states we can't perform any # If the compute server is in any of these states we can't perform any
@ -124,6 +118,9 @@ SERVER_INVALID_ACTION_STATUSES = ["BUILD", "REBOOT", "REBUILD"]
# Statuses in which an instance can have an action performed. # Statuses in which an instance can have an action performed.
VALID_ACTION_STATUSES = ["ACTIVE"] VALID_ACTION_STATUSES = ["ACTIVE"]
# Invalid states to contact the agent
AGENT_INVALID_STATUSES = ["BUILD", "REBOOT", "RESIZE"]
class SimpleInstance(object): class SimpleInstance(object):
"""A simple view of an instance. """A simple view of an instance.
@ -168,24 +165,6 @@ class SimpleInstance(object):
"""True if the service status indicates MySQL is up and running.""" """True if the service status indicates MySQL is up and running."""
return self.service_status.status in MYSQL_RESPONSIVE_STATUSES return self.service_status.status in MYSQL_RESPONSIVE_STATUSES
@staticmethod
def load(context, id):
try:
db_info = DBInstance.find_by(id=id)
except ModelNotFoundError:
raise rd_exceptions.NotFound(uuid=id)
service_status = InstanceServiceStatus.find_by(instance_id=id)
LOG.info("service status=%s" % service_status)
# TODO(tim.simpson): In the future, we'll listen to notifications and
# update the RDL database when the server status changes, and add
# server_status as a property to db_info, but for now we have to resort
# to this.
db_info = DBInstance.find_by(id=id)
if not context.is_admin and db_info.tenant_id != context.tenant:
raise rd_exceptions.NotFound(uuid=id)
load_simple_instance_server_status(context, db_info)
return SimpleInstance(context, db_info, service_status)
@property @property
def name(self): def name(self):
return self.db_info.name return self.db_info.name
@ -234,19 +213,46 @@ class SimpleInstance(object):
return self.db_info.volume_size return self.db_info.volume_size
def load_instance(cls, context, id, needs_server=False): class DetailInstance(SimpleInstance):
"""A detailed view of an Instnace.
This loads a SimpleInstance and then adds additional data for the
instance from the guest.
"""
def __init__(self, context, db_info, service_status):
super(DetailInstance, self).__init__(context, db_info, service_status)
self._volume_used = None
@property
def volume_used(self):
return self._volume_used
@volume_used.setter
def volume_used(self, value):
self._volume_used = value
def get_db_info(context, id):
if context is None: if context is None:
raise TypeError("Argument context not defined.") raise TypeError("Argument context not defined.")
elif id is None: elif id is None:
raise TypeError("Argument id not defined.") raise TypeError("Argument id not defined.")
try: try:
db_info = DBInstance.find_by(id=id) db_info = DBInstance.find_by(id=id)
except rd_exceptions.NotFound: except exception.NotFound:
raise rd_exceptions.NotFound(uuid=id) raise exception.NotFound(uuid=id)
except exception.ModelNotFoundError:
raise exception.NotFound(uuid=id)
if not context.is_admin and db_info.tenant_id != context.tenant: if not context.is_admin and db_info.tenant_id != context.tenant:
LOG.error("Tenant %s tried to access instance %s, owned by %s." LOG.error("Tenant %s tried to access instance %s, owned by %s."
% (context.tenant, id, db_info.tenant_id)) % (context.tenant, id, db_info.tenant_id))
raise rd_exceptions.NotFound(uuid=id) raise exception.NotFound(uuid=id)
return db_info
def load_instance(cls, context, id, needs_server=False):
db_info = get_db_info(context, id)
if not needs_server: if not needs_server:
# TODO(tim.simpson): When we have notifications this won't be # TODO(tim.simpson): When we have notifications this won't be
# necessary and instead we'll just use the server_status field from # necessary and instead we'll just use the server_status field from
@ -260,17 +266,38 @@ def load_instance(cls, context, id, needs_server=False):
#TODO(tim.simpson): Remove this hack when we have notifications! #TODO(tim.simpson): Remove this hack when we have notifications!
db_info.server_status = server.status db_info.server_status = server.status
db_info.addresses = server.addresses db_info.addresses = server.addresses
except rd_exceptions.ComputeInstanceNotFound: except exception.ComputeInstanceNotFound:
LOG.error("COMPUTE ID = %s" % db_info.compute_instance_id) LOG.error("COMPUTE ID = %s" % db_info.compute_instance_id)
raise rd_exceptions.UnprocessableEntity( raise exception.UnprocessableEntity("Instance %s is not ready." %
"Instance %s is not ready." % id) id)
task_status = db_info.task_status
service_status = InstanceServiceStatus.find_by(instance_id=id) service_status = InstanceServiceStatus.find_by(instance_id=id)
LOG.info("service status=%s" % service_status) LOG.info("service status=%s" % service_status)
return cls(context, db_info, server, service_status) return cls(context, db_info, server, service_status)
def load_instance_with_guest(cls, context, id):
db_info = get_db_info(context, id)
load_simple_instance_server_status(context, db_info)
service_status = InstanceServiceStatus.find_by(instance_id=id)
LOG.info("service status=%s" % service_status)
instance = cls(context, db_info, service_status)
try:
agent = agent_models.AgentHeartBeat.find_by(instance_id=id)
except exception.ModelNotFoundError as mnfe:
LOG.warn(mnfe)
return instance
if instance.status not in AGENT_INVALID_STATUSES and \
agent_models.AgentHeartBeat.is_active(agent):
guest = create_guest_client(context, id)
try:
instance.volume_used = guest.get_volume_info()['used']
except Exception as e:
LOG.error(e)
return instance
class BaseInstance(SimpleInstance): class BaseInstance(SimpleInstance):
"""Represents an instance.""" """Represents an instance."""
@ -334,7 +361,7 @@ class Instance(BuiltInstance):
def delete(self, force=False): def delete(self, force=False):
if not force and \ if not force and \
self.db_info.server_status in SERVER_INVALID_ACTION_STATUSES: self.db_info.server_status in SERVER_INVALID_ACTION_STATUSES:
raise rd_exceptions.UnprocessableEntity("Instance %s is not ready." raise exception.UnprocessableEntity("Instance %s is not ready."
% self.id) % self.id)
LOG.debug(_(" ... deleting compute id = %s") % LOG.debug(_(" ... deleting compute id = %s") %
self.db_info.compute_instance_id) self.db_info.compute_instance_id)
@ -349,7 +376,7 @@ class Instance(BuiltInstance):
try: try:
flavor = client.flavors.get(flavor_id) flavor = client.flavors.get(flavor_id)
except nova_exceptions.NotFound: except nova_exceptions.NotFound:
raise rd_exceptions.FlavorNotFound(uuid=flavor_id) raise exception.FlavorNotFound(uuid=flavor_id)
db_info = DBInstance.create(name=name, db_info = DBInstance.create(name=name,
flavor_id=flavor_id, tenant_id=context.tenant, flavor_id=flavor_id, tenant_id=context.tenant,
@ -375,7 +402,7 @@ class Instance(BuiltInstance):
msg = "Instance is not currently available for an action to be " \ msg = "Instance is not currently available for an action to be " \
"performed. Status [%s]" "performed. Status [%s]"
LOG.debug(_(msg) % self.status) LOG.debug(_(msg) % self.status)
raise rd_exceptions.UnprocessableEntity(_(msg) % self.status) raise exception.UnprocessableEntity(_(msg) % self.status)
def resize_flavor(self, new_flavor_id): def resize_flavor(self, new_flavor_id):
self.validate_can_perform_resize() self.validate_can_perform_resize()
@ -387,12 +414,12 @@ class Instance(BuiltInstance):
try: try:
new_flavor = client.flavors.get(new_flavor_id) new_flavor = client.flavors.get(new_flavor_id)
except nova_exceptions.NotFound: except nova_exceptions.NotFound:
raise rd_exceptions.FlavorNotFound(uuid=new_flavor_id) raise exception.FlavorNotFound(uuid=new_flavor_id)
old_flavor = client.flavors.get(self.flavor_id) old_flavor = client.flavors.get(self.flavor_id)
new_flavor_size = new_flavor.ram new_flavor_size = new_flavor.ram
old_flavor_size = old_flavor.ram old_flavor_size = old_flavor.ram
if new_flavor_size == old_flavor_size: if new_flavor_size == old_flavor_size:
raise rd_exceptions.CannotResizeToSameSize() raise exception.CannotResizeToSameSize()
# Set the task to RESIZING and begin the async call before returning. # Set the task to RESIZING and begin the async call before returning.
self.update_db(task_status=InstanceTasks.RESIZING) self.update_db(task_status=InstanceTasks.RESIZING)
@ -403,24 +430,22 @@ class Instance(BuiltInstance):
def resize_volume(self, new_size): def resize_volume(self, new_size):
LOG.info("Resizing volume of instance %s..." % self.id) LOG.info("Resizing volume of instance %s..." % self.id)
if not self.volume_size: if not self.volume_size:
raise rd_exceptions.BadRequest("Instance %s has no volume." raise exception.BadRequest("Instance %s has no volume." % self.id)
% self.id)
old_size = self.volume_size old_size = self.volume_size
if int(new_size) <= old_size: if int(new_size) <= old_size:
raise rd_exceptions.BadRequest("The new volume 'size' cannot be " raise exception.BadRequest("The new volume 'size' cannot be "
"less than the current volume size of '%s'" % old_size) "less than the current volume size of '%s'" % old_size)
# Set the task to Resizing before sending off to the taskmanager # Set the task to Resizing before sending off to the taskmanager
self.update_db(task_status=InstanceTasks.RESIZING) self.update_db(task_status=InstanceTasks.RESIZING)
task_api.API(self.context).resize_volume(new_size, self.id) task_api.API(self.context).resize_volume(new_size, self.id)
def restart(self): def restart(self):
if self.db_info.server_status in SERVER_INVALID_ACTION_STATUSES: if self.db_info.server_status in SERVER_INVALID_ACTION_STATUSES:
msg = _("Restart instance not allowed while instance %s is in %s " msg = _("Restart instance not allowed while instance %s is in %s "
"status.") % (self.id, instance_state) "status.") % (self.id, instance_state)
LOG.debug(msg) LOG.debug(msg)
# If the state is building then we throw an exception back # If the state is building then we throw an exception back
raise rd_exceptions.UnprocessableEntity(msg) raise exception.UnprocessableEntity(msg)
else: else:
LOG.info("Restarting instance %s..." % self.id) LOG.info("Restarting instance %s..." % self.id)
# Set our local status since Nova might not change it quick enough. # Set our local status since Nova might not change it quick enough.
@ -442,7 +467,7 @@ class Instance(BuiltInstance):
"performed (task status was %s, service status was %s)." \ "performed (task status was %s, service status was %s)." \
% (self.db_info.task_status, self.service_status.status) % (self.db_info.task_status, self.service_status.status)
LOG.error(msg) LOG.error(msg)
raise rd_exceptions.UnprocessableEntity(msg) raise exception.UnprocessableEntity(msg)
def validate_can_perform_resize(self): def validate_can_perform_resize(self):
""" """
@ -452,7 +477,7 @@ class Instance(BuiltInstance):
msg = "Instance is not currently available for an action to be " \ msg = "Instance is not currently available for an action to be " \
"performed (status was %s)." % self.status "performed (status was %s)." % self.status
LOG.error(msg) LOG.error(msg)
raise rd_exceptions.UnprocessableEntity(msg) raise exception.UnprocessableEntity(msg)
def create_server_list_matcher(server_list): def create_server_list_matcher(server_list):
@ -465,13 +490,13 @@ def create_server_list_matcher(server_list):
# The instance was not found in the list and # The instance was not found in the list and
# this can happen if the instance is deleted from # this can happen if the instance is deleted from
# nova but still in reddwarf database # nova but still in reddwarf database
raise rd_exceptions.ComputeInstanceNotFound( raise exception.ComputeInstanceNotFound(
instance_id=instance_id, server_id=server_id) instance_id=instance_id, server_id=server_id)
else: else:
# Should never happen, but never say never. # Should never happen, but never say never.
LOG.error(_("Server %s for instance %s was found twice!") LOG.error(_("Server %s for instance %s was found twice!")
% (server_id, instance_id)) % (server_id, instance_id))
raise rd_exceptions.ReddwarfError(uuid=instance_id) raise exception.ReddwarfError(uuid=instance_id)
return find_server return find_server
@ -481,6 +506,10 @@ class Instances(object):
@staticmethod @staticmethod
def load(context): def load(context):
def load_simple_instance(context, db, status):
return SimpleInstance(context, db, status)
if context is None: if context is None:
raise TypeError("Argument context not defined.") raise TypeError("Argument context not defined.")
client = create_nova_client(context) client = create_nova_client(context)
@ -495,12 +524,20 @@ class Instances(object):
marker=context.marker) marker=context.marker)
next_marker = data_view.next_page_marker next_marker = data_view.next_page_marker
ret = []
find_server = create_server_list_matcher(servers) find_server = create_server_list_matcher(servers)
for db in db_infos: for db in db_infos:
LOG.debug("checking for db [id=%s, compute_instance_id=%s]" % LOG.debug("checking for db [id=%s, compute_instance_id=%s]" %
(db.id, db.compute_instance_id)) (db.id, db.compute_instance_id))
for db in data_view.collection: ret = Instances._load_servers_status(load_simple_instance, context,
data_view.collection,
find_server)
return ret, next_marker
@staticmethod
def _load_servers_status(load_instance, context, db_items, find_server):
ret = []
for db in db_items:
server = None
try: try:
#TODO(tim.simpson): Delete when we get notifications working! #TODO(tim.simpson): Delete when we get notifications working!
if InstanceTasks.BUILDING == db.task_status: if InstanceTasks.BUILDING == db.task_status:
@ -509,7 +546,7 @@ class Instances(object):
try: try:
server = find_server(db.id, db.compute_instance_id) server = find_server(db.id, db.compute_instance_id)
db.server_status = server.status db.server_status = server.status
except rd_exceptions.ComputeInstanceNotFound: except exception.ComputeInstanceNotFound:
if InstanceTasks.DELETING == db.task_status: if InstanceTasks.DELETING == db.task_status:
#TODO(tim.simpson): This instance is actually #TODO(tim.simpson): This instance is actually
# deleted, but without notifications we never # deleted, but without notifications we never
@ -526,82 +563,15 @@ class Instances(object):
LOG.error(_("Server status could not be read for " LOG.error(_("Server status could not be read for "
"instance id(%s)") % (db.id)) "instance id(%s)") % (db.id))
continue continue
except ModelNotFoundError: except exception.ModelNotFoundError:
LOG.error(_("Server status could not be read for " LOG.error(_("Server status could not be read for "
"instance id(%s)") % (db.id)) "instance id(%s)") % (db.id))
continue continue
ret.append(SimpleInstance(context, db, status)) ret.append(load_instance(context, db, status))
return ret, next_marker return ret
class DatabaseModelBase(ModelBase): class DBInstance(dbmodels.DatabaseModelBase):
_auto_generated_attrs = ['id']
@classmethod
def create(cls, **values):
values['id'] = utils.generate_uuid()
values['created'] = utils.utcnow()
instance = cls(**values).save()
if not instance.is_valid():
raise InvalidModelError(instance.errors)
return instance
def save(self):
if not self.is_valid():
raise InvalidModelError(self.errors)
self['updated'] = utils.utcnow()
LOG.debug(_("Saving %s: %s") %
(self.__class__.__name__, self.__dict__))
return db.db_api.save(self)
def delete(self):
self['updated'] = utils.utcnow()
LOG.debug(_("Deleting %s: %s") %
(self.__class__.__name__, self.__dict__))
return db.db_api.delete(self)
def __init__(self, **kwargs):
self.merge_attributes(kwargs)
if not self.is_valid():
raise InvalidModelError(self.errors)
def merge_attributes(self, values):
"""dict.update() behaviour."""
for k, v in values.iteritems():
self[k] = v
@classmethod
def find_by(cls, **conditions):
model = cls.get_by(**conditions)
if model is None:
raise ModelNotFoundError(_("%s Not Found") % cls.__name__)
return model
@classmethod
def get_by(cls, **kwargs):
return db.db_api.find_by(cls, **cls._process_conditions(kwargs))
@classmethod
def find_all(cls, **kwargs):
return db.db_query.find_all(cls, **cls._process_conditions(kwargs))
@classmethod
def _process_conditions(cls, raw_conditions):
"""Override in inheritors to format/modify any conditions."""
return raw_conditions
@classmethod
def find_by_pagination(cls, collection_type, collection_query,
paginated_url, **kwargs):
elements, next_marker = collection_query.paginated_collection(**kwargs)
return pagination.PaginatedDataView(collection_type,
elements,
paginated_url,
next_marker)
class DBInstance(DatabaseModelBase):
"""Defines the task being executed plus the start time.""" """Defines the task being executed plus the start time."""
#TODO(tim.simpson): Add start time. #TODO(tim.simpson): Add start time.
@ -632,13 +602,13 @@ class DBInstance(DatabaseModelBase):
task_status = property(get_task_status, set_task_status) task_status = property(get_task_status, set_task_status)
class ServiceImage(DatabaseModelBase): class ServiceImage(dbmodels.DatabaseModelBase):
"""Defines the status of the service being run.""" """Defines the status of the service being run."""
_data_fields = ['service_name', 'image_id'] _data_fields = ['service_name', 'image_id']
class InstanceServiceStatus(DatabaseModelBase): class InstanceServiceStatus(dbmodels.DatabaseModelBase):
_data_fields = ['instance_id', 'status_id', 'status_description'] _data_fields = ['instance_id', 'status_id', 'status_description']
@ -672,19 +642,6 @@ def persisted_models():
} }
class InvalidModelError(rd_exceptions.ReddwarfError):
message = _("The following values are invalid: %(errors)s")
def __init__(self, errors, message=None):
super(InvalidModelError, self).__init__(message, errors=errors)
class ModelNotFoundError(rd_exceptions.ReddwarfError):
message = _("Not Found")
class ServiceStatus(object): class ServiceStatus(object):
"""Represents the status of the app and in some rare cases the agent. """Represents the status of the app and in some rare cases the agent.

View File

@ -40,7 +40,7 @@ class BaseController(wsgi.Controller):
exception.UnprocessableEntity, exception.UnprocessableEntity,
], ],
webob.exc.HTTPBadRequest: [ webob.exc.HTTPBadRequest: [
models.InvalidModelError, exception.InvalidModelError,
exception.BadRequest, exception.BadRequest,
exception.CannotResizeToSameSize, exception.CannotResizeToSameSize,
exception.BadValue exception.BadValue
@ -48,7 +48,7 @@ class BaseController(wsgi.Controller):
webob.exc.HTTPNotFound: [ webob.exc.HTTPNotFound: [
exception.NotFound, exception.NotFound,
exception.ComputeInstanceNotFound, exception.ComputeInstanceNotFound,
models.ModelNotFoundError, exception.ModelNotFoundError,
], ],
webob.exc.HTTPConflict: [ webob.exc.HTTPConflict: [
], ],
@ -191,8 +191,8 @@ class InstanceController(BaseController):
LOG.info(_("id : '%s'\n\n") % id) LOG.info(_("id : '%s'\n\n") % id)
context = req.environ[wsgi.CONTEXT_KEY] context = req.environ[wsgi.CONTEXT_KEY]
server = models.SimpleInstance.load(context=context, id=id) server = models.load_instance_with_guest(models.DetailInstance,
# TODO(cp16net): need to set the return code correctly context, id)
return wsgi.Result(views.InstanceDetailView(server, req=req, return wsgi.Result(views.InstanceDetailView(server, req=req,
add_addresses=self.add_addresses, add_addresses=self.add_addresses,
add_volumes=self.add_volumes).data(), 200) add_volumes=self.add_volumes).data(), 200)

View File

@ -18,9 +18,8 @@
import logging import logging
from reddwarf.common import config from reddwarf.common import config
from reddwarf.common import utils from reddwarf.common import utils
from reddwarf.common import wsgi
from reddwarf.common.views import create_links from reddwarf.common.views import create_links
from reddwarf.instance import models
LOG = logging.getLogger(__name__) LOG = logging.getLogger(__name__)
@ -87,6 +86,8 @@ class InstanceDetailView(InstanceView):
self.add_addresses = add_addresses self.add_addresses = add_addresses
self.add_volumes = add_volumes self.add_volumes = add_volumes
def _to_gb(self, bytes):
return bytes / 1024.0 ** 3
def data(self): def data(self):
result = super(InstanceDetailView, self).data() result = super(InstanceDetailView, self).data()
@ -97,6 +98,11 @@ class InstanceDetailView(InstanceView):
ip = get_ip_address(self.instance.addresses) ip = get_ip_address(self.instance.addresses)
if ip is not None and len(ip) > 0: if ip is not None and len(ip) > 0:
result['instance']['ip'] = ip result['instance']['ip'] = ip
if self.add_volumes:
if isinstance(self.instance, models.DetailInstance) and \
self.instance.volume_used:
used = self._to_gb(self.instance.volume_used)
result['instance']['volume']['used'] = used
return result return result

View File

@ -50,6 +50,7 @@ class API(object):
from reddwarf.taskmanager.manager import TaskManager from reddwarf.taskmanager.manager import TaskManager
instance = TaskManager() instance = TaskManager()
method = getattr(instance, method_name) method = getattr(instance, method_name)
def func(): def func():
try: try:
method(self.context, **kwargs) method(self.context, **kwargs)

View File

@ -79,5 +79,3 @@ class TaskManager(service.Manager):
instance_tasks = FreshInstanceTasks.load(context, instance_id) instance_tasks = FreshInstanceTasks.load(context, instance_id)
instance_tasks.create_instance(flavor_id, flavor_ram, image_id, instance_tasks.create_instance(flavor_id, flavor_ram, image_id,
databases, service_type, volume_size) databases, service_type, volume_size)

View File

@ -106,7 +106,6 @@ class FreshInstanceTasks(FreshInstance):
LOG.debug("block_device = %s" % block_device) LOG.debug("block_device = %s" % block_device)
LOG.debug("volume = %s" % volumes) LOG.debug("volume = %s" % volumes)
device_path = config.Config.get('device_path', '/dev/vdb') device_path = config.Config.get('device_path', '/dev/vdb')
mount_point = config.Config.get('mount_point', '/var/lib/mysql') mount_point = config.Config.get('mount_point', '/var/lib/mysql')
LOG.debug(_("device_path = %s") % device_path) LOG.debug(_("device_path = %s") % device_path)
@ -146,6 +145,7 @@ class FreshInstanceTasks(FreshInstance):
nova_client = create_nova_client(self.context) nova_client = create_nova_client(self.context)
if utils.bool_from_string(dns_support): if utils.bool_from_string(dns_support):
def get_server(): def get_server():
return nova_client.servers.get(self.db_info.compute_instance_id) return nova_client.servers.get(self.db_info.compute_instance_id)

View File

@ -98,11 +98,13 @@ class FakeGuest(object):
mount_point=None): mount_point=None):
from reddwarf.instance.models import InstanceServiceStatus from reddwarf.instance.models import InstanceServiceStatus
from reddwarf.instance.models import ServiceStatuses from reddwarf.instance.models import ServiceStatuses
from reddwarf.guestagent.models import AgentHeartBeat
def update_db(): def update_db():
status = InstanceServiceStatus.find_by(instance_id=self.id) status = InstanceServiceStatus.find_by(instance_id=self.id)
status.status = ServiceStatuses.RUNNING status.status = ServiceStatuses.RUNNING
status.save() status.save()
AgentHeartBeat.create(instance_id=self.id)
EventSimulator.add_event(2.0, update_db) EventSimulator.add_event(2.0, update_db)
def restart(self): def restart(self):
@ -125,6 +127,10 @@ class FakeGuest(object):
status.status = ServiceStatuses.SHUTDOWN status.status = ServiceStatuses.SHUTDOWN
status.save() status.save()
def get_volume_info(self):
"""Return used volume information in bytes."""
return {'used': 175756487}
def get_or_create(id): def get_or_create(id):
if id not in DB: if id not in DB:

View File

@ -289,8 +289,8 @@ class FakeVolume(object):
for attachment in self.attachments: for attachment in self.attachments:
if attachment['server_id'] == server_id: if attachment['server_id'] == server_id:
return # Do nothing return # Do nothing
self.attachments.append({'server_id':server_id, self.attachments.append({'server_id': server_id,
'device':self.device}) 'device': self.device})
@property @property
def status(self): def status(self):
@ -348,6 +348,7 @@ class FakeVolumes(object):
def resize(self, volume_id, new_size): def resize(self, volume_id, new_size):
volume = self.get(volume_id) volume = self.get(volume_id)
def finish_resize(): def finish_resize():
volume._current_status = "in-use" volume._current_status = "in-use"
volume.size = new_size volume.size = new_size