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):
|
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()
|
||||||
|
@ -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
|
||||||
|
@ -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
|
||||||
|
@ -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
|
||||||
|
@ -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")
|
||||||
|
@ -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
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))
|
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):
|
||||||
|
@ -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 = {}
|
||||||
|
@ -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
|
||||||
|
@ -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)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@ -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: [
|
||||||
],
|
],
|
||||||
|
@ -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):
|
||||||
|
@ -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")
|
||||||
|
@ -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())
|
||||||
|
|
||||||
|
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):
|
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 []
|
||||||
|
@ -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.
|
||||||
|
|
||||||
|
@ -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)
|
||||||
|
@ -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
|
||||||
|
|
||||||
|
|
||||||
|
@ -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)
|
||||||
|
@ -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)
|
||||||
|
|
||||||
|
|
||||||
|
@ -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)
|
||||||
|
|
||||||
|
@ -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:
|
||||||
|
@ -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
|
||||||
|
Loading…
Reference in New Issue
Block a user