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:
parent
3380bbb738
commit
c7d93e7ce7
@ -74,7 +74,8 @@ class Commands(object):
|
||||
|
||||
def image_update(self, service_name, image_id):
|
||||
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:
|
||||
# Create a new one
|
||||
image = instance_models.ServiceImage()
|
||||
|
@ -33,6 +33,9 @@ nova_volume_url = http://localhost:8776/v1
|
||||
|
||||
# Config options for enabling volume service
|
||||
reddwarf_volume_support = True
|
||||
block_device_mapping = /var/lib/mysql
|
||||
device_path = /var/lib/mysql
|
||||
mount_point = /var/lib/mysql
|
||||
volume_time_out=30
|
||||
|
||||
# Configuration options for talking to nova via the novaclient.
|
||||
@ -50,6 +53,11 @@ taskmanager_manager=reddwarf.taskmanager.manager.TaskManager
|
||||
# Reddwarf DNS
|
||||
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 ========================
|
||||
|
||||
notifier_queue_hostname = localhost
|
||||
|
@ -47,7 +47,7 @@ add_addresses = True
|
||||
# Config options for enabling volume service
|
||||
reddwarf_volume_support = True
|
||||
block_device_mapping = /var/lib/mysql
|
||||
device_path = /dev/vdb
|
||||
device_path = /var/lib/mysql
|
||||
mount_point = /var/lib/mysql
|
||||
max_accepted_volume_size = 10
|
||||
volume_time_out=30
|
||||
@ -58,6 +58,11 @@ reddwarf_dns_support = False
|
||||
# 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 ========================
|
||||
|
||||
notifier_queue_hostname = localhost
|
||||
|
@ -64,6 +64,14 @@ mount_point = /var/lib/mysql
|
||||
max_accepted_volume_size = 10
|
||||
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 ========================
|
||||
|
||||
notifier_queue_hostname = localhost
|
||||
|
@ -88,6 +88,11 @@ class GuestError(ReddwarfError):
|
||||
"%(original_message)s.")
|
||||
|
||||
|
||||
class GuestTimeout(ReddwarfError):
|
||||
|
||||
message = _("Timeout trying to connect to the Guest Agent.")
|
||||
|
||||
|
||||
class BadRequest(ReddwarfError):
|
||||
|
||||
message = _("The server could not comply with the request since it is "
|
||||
@ -147,3 +152,13 @@ class PollTimeOut(ReddwarfError):
|
||||
|
||||
class Forbidden(ReddwarfError):
|
||||
message = _("User does not have admin privileges.")
|
||||
|
||||
|
||||
class InvalidModelError(ReddwarfError):
|
||||
|
||||
message = _("The following values are invalid: %(errors)s")
|
||||
|
||||
|
||||
class ModelNotFoundError(NotFound):
|
||||
|
||||
message = _("Not Found")
|
||||
|
@ -48,7 +48,8 @@ LOG = logging.getLogger('reddwarf.common.wsgi')
|
||||
XMLNS = 'http://docs.openstack.org/database/api/v1.0'
|
||||
CUSTOM_PLURALS_METADATA = {'databases': '', 'users': ''}
|
||||
CUSTOM_SERIALIZER_METADATA = {'instance': {'status': '', 'hostname': '',
|
||||
'id':'', 'name':'','created':'', 'updated':''},
|
||||
'id': '', 'name': '', 'created': '',
|
||||
'updated': ''},
|
||||
'volume': {'size': '', 'used': ''},
|
||||
'flavor': {'id': '', 'ram': '', 'name': ''},
|
||||
'link': {'href': '', 'rel': ''},
|
||||
|
92
reddwarf/db/models.py
Normal file
92
reddwarf/db/models.py
Normal 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)
|
@ -36,6 +36,8 @@ def map(engine, models):
|
||||
Table('service_statuses', meta, autoload=True))
|
||||
orm.mapper(models['dns_records'],
|
||||
Table('dns_records', meta, autoload=True))
|
||||
orm.mapper(models['agent_heartbeats'],
|
||||
Table('agent_heartbeats', meta, autoload=True))
|
||||
|
||||
|
||||
def mapping_exists(model):
|
||||
|
@ -42,11 +42,13 @@ def configure_db(options, models_mapper=None):
|
||||
from reddwarf.instance import models as base_models
|
||||
from reddwarf.dns import models as dns_models
|
||||
from reddwarf.extensions.mysql import models as mysql_models
|
||||
from reddwarf.guestagent import models as agent_models
|
||||
|
||||
model_modules = [
|
||||
base_models,
|
||||
dns_models,
|
||||
mysql_models,
|
||||
agent_models,
|
||||
]
|
||||
|
||||
models = {}
|
||||
|
@ -22,9 +22,8 @@ Model classes that map instance Ip to dns record.
|
||||
import logging
|
||||
|
||||
from reddwarf import db
|
||||
from reddwarf.common import exception
|
||||
from reddwarf.common.models import ModelBase
|
||||
from reddwarf.instance.models import InvalidModelError
|
||||
from reddwarf.instance.models import ModelNotFoundError
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
|
||||
@ -48,12 +47,12 @@ class DnsRecord(ModelBase):
|
||||
def create(cls, **values):
|
||||
record = cls(**values).save()
|
||||
if not record.is_valid():
|
||||
raise InvalidModelError(record.errors)
|
||||
raise exception.InvalidModelError(errors=record.errors)
|
||||
return record
|
||||
|
||||
def save(self):
|
||||
if not self.is_valid():
|
||||
raise InvalidModelError(self.errors)
|
||||
raise exception.InvalidModelError(errors=self.errors)
|
||||
LOG.debug(_("Saving %s: %s") %
|
||||
(self.__class__.__name__, self.__dict__))
|
||||
return db.db_api.save(self)
|
||||
@ -67,7 +66,8 @@ class DnsRecord(ModelBase):
|
||||
def find_by(cls, **conditions):
|
||||
model = cls.get_by(**conditions)
|
||||
if model is None:
|
||||
raise ModelNotFoundError(_("%s Not Found") % cls.__name__)
|
||||
raise exception.ModelNotFoundError(_("%s Not Found") %
|
||||
cls.__name__)
|
||||
return model
|
||||
|
||||
@classmethod
|
||||
|
@ -218,6 +218,3 @@ class RsDnsZone(object):
|
||||
|
||||
def __str__(self):
|
||||
return "%s:%s" % (self.id, self.name)
|
||||
|
||||
|
||||
|
||||
|
@ -22,7 +22,6 @@ from reddwarf.common import exception
|
||||
from reddwarf.common import pagination
|
||||
from reddwarf.common import wsgi
|
||||
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 views
|
||||
|
||||
@ -44,7 +43,7 @@ class BaseController(wsgi.Controller):
|
||||
],
|
||||
webob.exc.HTTPNotFound: [
|
||||
exception.NotFound,
|
||||
instance_models.ModelNotFoundError,
|
||||
exception.ModelNotFoundError,
|
||||
],
|
||||
webob.exc.HTTPConflict: [
|
||||
],
|
||||
|
@ -20,7 +20,7 @@
|
||||
from reddwarf import db
|
||||
|
||||
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.models import NovaRemoteModelBase
|
||||
from reddwarf.common.remote import create_nova_client
|
||||
@ -39,13 +39,13 @@ class Flavor(object):
|
||||
client = create_nova_client(context)
|
||||
self.flavor = client.flavors.get(flavor_id)
|
||||
except nova_exceptions.NotFound, e:
|
||||
raise rd_exceptions.NotFound(uuid=flavor_id)
|
||||
raise exception.NotFound(uuid=flavor_id)
|
||||
except nova_exceptions.ClientException, e:
|
||||
raise rd_exceptions.ReddwarfError(str(e))
|
||||
raise exception.ReddwarfError(str(e))
|
||||
return
|
||||
msg = ("Flavor is not defined, and"
|
||||
" context and flavor_id were not specified.")
|
||||
raise InvalidModelError(msg)
|
||||
raise exception.InvalidModelError(errors=msg)
|
||||
|
||||
@property
|
||||
def id(self):
|
||||
|
@ -19,16 +19,19 @@
|
||||
Handles all request to the Platform or Guest VM
|
||||
"""
|
||||
|
||||
|
||||
import logging
|
||||
|
||||
from eventlet import Timeout
|
||||
|
||||
from reddwarf import rpc
|
||||
from reddwarf.common import config
|
||||
from reddwarf.common import exception
|
||||
from reddwarf.common import utils
|
||||
# from nova.db import api as dbapi
|
||||
|
||||
|
||||
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):
|
||||
@ -38,22 +41,30 @@ class API(object):
|
||||
self.context = context
|
||||
self.id = id
|
||||
|
||||
def _call(self, method_name, **kwargs):
|
||||
def _call(self, method_name, timeout_sec, **kwargs):
|
||||
LOG.debug("Calling %s" % method_name)
|
||||
|
||||
timeout = Timeout(timeout_sec)
|
||||
try:
|
||||
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)
|
||||
return result
|
||||
except Exception as e:
|
||||
LOG.error(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):
|
||||
try:
|
||||
rpc.cast(self.context, self._get_routing_key(),
|
||||
{"method": method_name,
|
||||
"args": kwargs})
|
||||
{'method': method_name, 'args': kwargs})
|
||||
except Exception as e:
|
||||
LOG.error(e)
|
||||
raise exception.GuestError(original_message=str(e))
|
||||
@ -61,7 +72,7 @@ class API(object):
|
||||
def _cast_with_consumer(self, method_name, **kwargs):
|
||||
try:
|
||||
rpc.cast_with_consumer(self.context, self._get_routing_key(),
|
||||
{"method": method_name, "args": kwargs})
|
||||
{'method': method_name, 'args': kwargs})
|
||||
except Exception as e:
|
||||
LOG.error(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):
|
||||
"""Make an asynchronous call to list database users"""
|
||||
LOG.debug(_("Listing Users for Instance %s"), self.id)
|
||||
return self._call("list_users", limit=limit, marker=marker,
|
||||
include_marker=include_marker)
|
||||
return self._call("list_users", AGENT_LOW_TIMEOUT, limit=limit,
|
||||
marker=marker, include_marker=include_marker)
|
||||
|
||||
def delete_user(self, 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):
|
||||
"""Make an asynchronous call to list databases"""
|
||||
LOG.debug(_("Listing databases for Instance %s"), self.id)
|
||||
return self._call("list_databases", limit=limit, marker=marker,
|
||||
include_marker=include_marker)
|
||||
return self._call("list_databases", AGENT_LOW_TIMEOUT, limit=limit,
|
||||
marker=marker, include_marker=include_marker)
|
||||
|
||||
def delete_database(self, 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
|
||||
access from anywhere"""
|
||||
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):
|
||||
"""Make a synchronous call to disable the root user for
|
||||
access from anywhere"""
|
||||
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):
|
||||
"""Make a synchronous call to check if root access is
|
||||
available for the container"""
|
||||
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):
|
||||
"""Make a synchronous call to get diagnostics for the container"""
|
||||
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,
|
||||
device_path='/dev/vdb', mount_point='/mnt/volume'):
|
||||
@ -139,20 +150,26 @@ class API(object):
|
||||
def restart(self):
|
||||
"""Restart the MySQL server."""
|
||||
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):
|
||||
"""Start the MySQL server."""
|
||||
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)
|
||||
|
||||
def stop_mysql(self):
|
||||
"""Stop the MySQL server."""
|
||||
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):
|
||||
"""Make an asynchronous call to self upgrade the guest agent"""
|
||||
LOG.debug(_("Sending an upgrade call to nova-guest"))
|
||||
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")
|
||||
|
@ -72,6 +72,7 @@ INCLUDE_MARKER_OPERATORS = {
|
||||
False: ">"
|
||||
}
|
||||
|
||||
|
||||
def generate_random_password():
|
||||
return str(uuid.uuid4())
|
||||
|
||||
|
65
reddwarf/guestagent/models.py
Normal file
65
reddwarf/guestagent/models.py
Normal 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)
|
@ -24,7 +24,8 @@ Intermediary class for building SQL queries for use by the guest agent.
|
||||
|
||||
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.tables = tables or []
|
||||
self.where = where or []
|
||||
|
@ -21,27 +21,21 @@ import eventlet
|
||||
import logging
|
||||
import netaddr
|
||||
|
||||
from reddwarf import db
|
||||
|
||||
from novaclient import exceptions as nova_exceptions
|
||||
from reddwarf.common import config
|
||||
from reddwarf.common import exception as rd_exceptions
|
||||
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 import exception
|
||||
from reddwarf.common.remote import create_dns_client
|
||||
from reddwarf.common.remote import create_guest_client
|
||||
from reddwarf.common.remote import create_nova_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 InstanceTasks
|
||||
from reddwarf.guestagent import models as agent_models
|
||||
from reddwarf.taskmanager import api as task_api
|
||||
|
||||
|
||||
from eventlet import greenthread
|
||||
from reddwarf.instance.views import get_ip_address
|
||||
|
||||
|
||||
CONFIG = config.Config
|
||||
@ -55,10 +49,10 @@ def load_server(context, instance_id, server_id):
|
||||
server = client.servers.get(server_id)
|
||||
except nova_exceptions.NotFound, e:
|
||||
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)
|
||||
except nova_exceptions.ClientException, e:
|
||||
raise rd_exceptions.ReddwarfError(str(e))
|
||||
raise exception.ReddwarfError(str(e))
|
||||
return server
|
||||
|
||||
|
||||
@ -80,7 +74,7 @@ def populate_databases(dbs):
|
||||
databases.append(mydb.serialize())
|
||||
return databases
|
||||
except ValueError as ve:
|
||||
raise rd_exceptions.BadRequest(ve.message)
|
||||
raise exception.BadRequest(ve.message)
|
||||
|
||||
|
||||
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
|
||||
# exception.
|
||||
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
|
||||
@ -124,6 +118,9 @@ SERVER_INVALID_ACTION_STATUSES = ["BUILD", "REBOOT", "REBUILD"]
|
||||
# Statuses in which an instance can have an action performed.
|
||||
VALID_ACTION_STATUSES = ["ACTIVE"]
|
||||
|
||||
# Invalid states to contact the agent
|
||||
AGENT_INVALID_STATUSES = ["BUILD", "REBOOT", "RESIZE"]
|
||||
|
||||
|
||||
class SimpleInstance(object):
|
||||
"""A simple view of an instance.
|
||||
@ -168,24 +165,6 @@ class SimpleInstance(object):
|
||||
"""True if the service status indicates MySQL is up and running."""
|
||||
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
|
||||
def name(self):
|
||||
return self.db_info.name
|
||||
@ -234,19 +213,46 @@ class SimpleInstance(object):
|
||||
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:
|
||||
raise TypeError("Argument context not defined.")
|
||||
elif id is None:
|
||||
raise TypeError("Argument id not defined.")
|
||||
try:
|
||||
db_info = DBInstance.find_by(id=id)
|
||||
except rd_exceptions.NotFound:
|
||||
raise rd_exceptions.NotFound(uuid=id)
|
||||
except exception.NotFound:
|
||||
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:
|
||||
LOG.error("Tenant %s tried to access instance %s, owned by %s."
|
||||
% (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:
|
||||
# TODO(tim.simpson): When we have notifications this won't be
|
||||
# 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!
|
||||
db_info.server_status = server.status
|
||||
db_info.addresses = server.addresses
|
||||
except rd_exceptions.ComputeInstanceNotFound:
|
||||
except exception.ComputeInstanceNotFound:
|
||||
LOG.error("COMPUTE ID = %s" % db_info.compute_instance_id)
|
||||
raise rd_exceptions.UnprocessableEntity(
|
||||
"Instance %s is not ready." % id)
|
||||
raise exception.UnprocessableEntity("Instance %s is not ready." %
|
||||
id)
|
||||
|
||||
task_status = db_info.task_status
|
||||
service_status = InstanceServiceStatus.find_by(instance_id=id)
|
||||
LOG.info("service status=%s" % 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):
|
||||
"""Represents an instance."""
|
||||
|
||||
@ -334,7 +361,7 @@ class Instance(BuiltInstance):
|
||||
def delete(self, force=False):
|
||||
if not force and \
|
||||
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)
|
||||
LOG.debug(_(" ... deleting compute id = %s") %
|
||||
self.db_info.compute_instance_id)
|
||||
@ -349,7 +376,7 @@ class Instance(BuiltInstance):
|
||||
try:
|
||||
flavor = client.flavors.get(flavor_id)
|
||||
except nova_exceptions.NotFound:
|
||||
raise rd_exceptions.FlavorNotFound(uuid=flavor_id)
|
||||
raise exception.FlavorNotFound(uuid=flavor_id)
|
||||
|
||||
db_info = DBInstance.create(name=name,
|
||||
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 " \
|
||||
"performed. Status [%s]"
|
||||
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):
|
||||
self.validate_can_perform_resize()
|
||||
@ -387,12 +414,12 @@ class Instance(BuiltInstance):
|
||||
try:
|
||||
new_flavor = client.flavors.get(new_flavor_id)
|
||||
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)
|
||||
new_flavor_size = new_flavor.ram
|
||||
old_flavor_size = old_flavor.ram
|
||||
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.
|
||||
self.update_db(task_status=InstanceTasks.RESIZING)
|
||||
@ -403,24 +430,22 @@ class Instance(BuiltInstance):
|
||||
def resize_volume(self, new_size):
|
||||
LOG.info("Resizing volume of instance %s..." % self.id)
|
||||
if not self.volume_size:
|
||||
raise rd_exceptions.BadRequest("Instance %s has no volume."
|
||||
% self.id)
|
||||
raise exception.BadRequest("Instance %s has no volume." % self.id)
|
||||
old_size = self.volume_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)
|
||||
# Set the task to Resizing before sending off to the taskmanager
|
||||
self.update_db(task_status=InstanceTasks.RESIZING)
|
||||
task_api.API(self.context).resize_volume(new_size, self.id)
|
||||
|
||||
|
||||
def restart(self):
|
||||
if self.db_info.server_status in SERVER_INVALID_ACTION_STATUSES:
|
||||
msg = _("Restart instance not allowed while instance %s is in %s "
|
||||
"status.") % (self.id, instance_state)
|
||||
LOG.debug(msg)
|
||||
# If the state is building then we throw an exception back
|
||||
raise rd_exceptions.UnprocessableEntity(msg)
|
||||
raise exception.UnprocessableEntity(msg)
|
||||
else:
|
||||
LOG.info("Restarting instance %s..." % self.id)
|
||||
# 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)." \
|
||||
% (self.db_info.task_status, self.service_status.status)
|
||||
LOG.error(msg)
|
||||
raise rd_exceptions.UnprocessableEntity(msg)
|
||||
raise exception.UnprocessableEntity(msg)
|
||||
|
||||
def validate_can_perform_resize(self):
|
||||
"""
|
||||
@ -452,7 +477,7 @@ class Instance(BuiltInstance):
|
||||
msg = "Instance is not currently available for an action to be " \
|
||||
"performed (status was %s)." % self.status
|
||||
LOG.error(msg)
|
||||
raise rd_exceptions.UnprocessableEntity(msg)
|
||||
raise exception.UnprocessableEntity(msg)
|
||||
|
||||
|
||||
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
|
||||
# this can happen if the instance is deleted from
|
||||
# nova but still in reddwarf database
|
||||
raise rd_exceptions.ComputeInstanceNotFound(
|
||||
raise exception.ComputeInstanceNotFound(
|
||||
instance_id=instance_id, server_id=server_id)
|
||||
else:
|
||||
# Should never happen, but never say never.
|
||||
LOG.error(_("Server %s for instance %s was found twice!")
|
||||
% (server_id, instance_id))
|
||||
raise rd_exceptions.ReddwarfError(uuid=instance_id)
|
||||
raise exception.ReddwarfError(uuid=instance_id)
|
||||
return find_server
|
||||
|
||||
|
||||
@ -481,6 +506,10 @@ class Instances(object):
|
||||
|
||||
@staticmethod
|
||||
def load(context):
|
||||
|
||||
def load_simple_instance(context, db, status):
|
||||
return SimpleInstance(context, db, status)
|
||||
|
||||
if context is None:
|
||||
raise TypeError("Argument context not defined.")
|
||||
client = create_nova_client(context)
|
||||
@ -495,12 +524,20 @@ class Instances(object):
|
||||
marker=context.marker)
|
||||
next_marker = data_view.next_page_marker
|
||||
|
||||
ret = []
|
||||
find_server = create_server_list_matcher(servers)
|
||||
for db in db_infos:
|
||||
LOG.debug("checking for db [id=%s, compute_instance_id=%s]" %
|
||||
(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:
|
||||
#TODO(tim.simpson): Delete when we get notifications working!
|
||||
if InstanceTasks.BUILDING == db.task_status:
|
||||
@ -509,7 +546,7 @@ class Instances(object):
|
||||
try:
|
||||
server = find_server(db.id, db.compute_instance_id)
|
||||
db.server_status = server.status
|
||||
except rd_exceptions.ComputeInstanceNotFound:
|
||||
except exception.ComputeInstanceNotFound:
|
||||
if InstanceTasks.DELETING == db.task_status:
|
||||
#TODO(tim.simpson): This instance is actually
|
||||
# deleted, but without notifications we never
|
||||
@ -526,82 +563,15 @@ class Instances(object):
|
||||
LOG.error(_("Server status could not be read for "
|
||||
"instance id(%s)") % (db.id))
|
||||
continue
|
||||
except ModelNotFoundError:
|
||||
except exception.ModelNotFoundError:
|
||||
LOG.error(_("Server status could not be read for "
|
||||
"instance id(%s)") % (db.id))
|
||||
continue
|
||||
ret.append(SimpleInstance(context, db, status))
|
||||
return ret, next_marker
|
||||
ret.append(load_instance(context, db, status))
|
||||
return ret
|
||||
|
||||
|
||||
class DatabaseModelBase(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 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):
|
||||
class DBInstance(dbmodels.DatabaseModelBase):
|
||||
"""Defines the task being executed plus the start time."""
|
||||
|
||||
#TODO(tim.simpson): Add start time.
|
||||
@ -632,13 +602,13 @@ class DBInstance(DatabaseModelBase):
|
||||
task_status = property(get_task_status, set_task_status)
|
||||
|
||||
|
||||
class ServiceImage(DatabaseModelBase):
|
||||
class ServiceImage(dbmodels.DatabaseModelBase):
|
||||
"""Defines the status of the service being run."""
|
||||
|
||||
_data_fields = ['service_name', 'image_id']
|
||||
|
||||
|
||||
class InstanceServiceStatus(DatabaseModelBase):
|
||||
class InstanceServiceStatus(dbmodels.DatabaseModelBase):
|
||||
|
||||
_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):
|
||||
"""Represents the status of the app and in some rare cases the agent.
|
||||
|
||||
|
@ -40,7 +40,7 @@ class BaseController(wsgi.Controller):
|
||||
exception.UnprocessableEntity,
|
||||
],
|
||||
webob.exc.HTTPBadRequest: [
|
||||
models.InvalidModelError,
|
||||
exception.InvalidModelError,
|
||||
exception.BadRequest,
|
||||
exception.CannotResizeToSameSize,
|
||||
exception.BadValue
|
||||
@ -48,7 +48,7 @@ class BaseController(wsgi.Controller):
|
||||
webob.exc.HTTPNotFound: [
|
||||
exception.NotFound,
|
||||
exception.ComputeInstanceNotFound,
|
||||
models.ModelNotFoundError,
|
||||
exception.ModelNotFoundError,
|
||||
],
|
||||
webob.exc.HTTPConflict: [
|
||||
],
|
||||
@ -191,8 +191,8 @@ class InstanceController(BaseController):
|
||||
LOG.info(_("id : '%s'\n\n") % id)
|
||||
|
||||
context = req.environ[wsgi.CONTEXT_KEY]
|
||||
server = models.SimpleInstance.load(context=context, id=id)
|
||||
# TODO(cp16net): need to set the return code correctly
|
||||
server = models.load_instance_with_guest(models.DetailInstance,
|
||||
context, id)
|
||||
return wsgi.Result(views.InstanceDetailView(server, req=req,
|
||||
add_addresses=self.add_addresses,
|
||||
add_volumes=self.add_volumes).data(), 200)
|
||||
|
@ -18,9 +18,8 @@
|
||||
import logging
|
||||
from reddwarf.common import config
|
||||
from reddwarf.common import utils
|
||||
from reddwarf.common import wsgi
|
||||
from reddwarf.common.views import create_links
|
||||
|
||||
from reddwarf.instance import models
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
|
||||
@ -87,6 +86,8 @@ class InstanceDetailView(InstanceView):
|
||||
self.add_addresses = add_addresses
|
||||
self.add_volumes = add_volumes
|
||||
|
||||
def _to_gb(self, bytes):
|
||||
return bytes / 1024.0 ** 3
|
||||
|
||||
def data(self):
|
||||
result = super(InstanceDetailView, self).data()
|
||||
@ -97,6 +98,11 @@ class InstanceDetailView(InstanceView):
|
||||
ip = get_ip_address(self.instance.addresses)
|
||||
if ip is not None and len(ip) > 0:
|
||||
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
|
||||
|
||||
|
||||
|
@ -50,6 +50,7 @@ class API(object):
|
||||
from reddwarf.taskmanager.manager import TaskManager
|
||||
instance = TaskManager()
|
||||
method = getattr(instance, method_name)
|
||||
|
||||
def func():
|
||||
try:
|
||||
method(self.context, **kwargs)
|
||||
|
@ -79,5 +79,3 @@ class TaskManager(service.Manager):
|
||||
instance_tasks = FreshInstanceTasks.load(context, instance_id)
|
||||
instance_tasks.create_instance(flavor_id, flavor_ram, image_id,
|
||||
databases, service_type, volume_size)
|
||||
|
||||
|
||||
|
@ -106,7 +106,6 @@ class FreshInstanceTasks(FreshInstance):
|
||||
LOG.debug("block_device = %s" % block_device)
|
||||
LOG.debug("volume = %s" % volumes)
|
||||
|
||||
|
||||
device_path = config.Config.get('device_path', '/dev/vdb')
|
||||
mount_point = config.Config.get('mount_point', '/var/lib/mysql')
|
||||
LOG.debug(_("device_path = %s") % device_path)
|
||||
@ -146,6 +145,7 @@ class FreshInstanceTasks(FreshInstance):
|
||||
|
||||
nova_client = create_nova_client(self.context)
|
||||
if utils.bool_from_string(dns_support):
|
||||
|
||||
def get_server():
|
||||
return nova_client.servers.get(self.db_info.compute_instance_id)
|
||||
|
||||
|
@ -98,11 +98,13 @@ class FakeGuest(object):
|
||||
mount_point=None):
|
||||
from reddwarf.instance.models import InstanceServiceStatus
|
||||
from reddwarf.instance.models import ServiceStatuses
|
||||
from reddwarf.guestagent.models import AgentHeartBeat
|
||||
|
||||
def update_db():
|
||||
status = InstanceServiceStatus.find_by(instance_id=self.id)
|
||||
status.status = ServiceStatuses.RUNNING
|
||||
status.save()
|
||||
AgentHeartBeat.create(instance_id=self.id)
|
||||
EventSimulator.add_event(2.0, update_db)
|
||||
|
||||
def restart(self):
|
||||
@ -125,6 +127,10 @@ class FakeGuest(object):
|
||||
status.status = ServiceStatuses.SHUTDOWN
|
||||
status.save()
|
||||
|
||||
def get_volume_info(self):
|
||||
"""Return used volume information in bytes."""
|
||||
return {'used': 175756487}
|
||||
|
||||
|
||||
def get_or_create(id):
|
||||
if id not in DB:
|
||||
|
@ -348,6 +348,7 @@ class FakeVolumes(object):
|
||||
|
||||
def resize(self, volume_id, new_size):
|
||||
volume = self.get(volume_id)
|
||||
|
||||
def finish_resize():
|
||||
volume._current_status = "in-use"
|
||||
volume.size = new_size
|
||||
|
Loading…
Reference in New Issue
Block a user