Created test fakes for Nova and the Guest. With these you can run the server with no external dependencies!
* Changed reddwarf-server to load the paste config first so the config file valeus would be read during start up. * Made the Config class update the dictionary values instead of replace them all. * Added a test conf file for running the server locally. * Added a fake version of Nova and the Guest API. * Changed Guest API to not require every single method to pass context and ID. This also makes it easier to fake. * Changed some instances of uuid to id. * Added "BUILD" server status to list of statuses which should be returned instantly in Instance status code.
This commit is contained in:
parent
7cb40c224b
commit
cf3e0b5042
@ -60,6 +60,7 @@ if __name__ == '__main__':
|
|||||||
create_options(oparser)
|
create_options(oparser)
|
||||||
(options, args) = config.parse_options(oparser)
|
(options, args) = config.parse_options(oparser)
|
||||||
try:
|
try:
|
||||||
|
config.Config.load_paste_config('reddwarf', options, args)
|
||||||
conf, app = config.Config.load_paste_app('reddwarf', options, args)
|
conf, app = config.Config.load_paste_app('reddwarf', options, args)
|
||||||
db_api.configure_db(conf)
|
db_api.configure_db(conf)
|
||||||
server = wsgi.Server()
|
server = wsgi.Server()
|
||||||
|
102
etc/reddwarf/reddwarf.conf.test
Normal file
102
etc/reddwarf/reddwarf.conf.test
Normal file
@ -0,0 +1,102 @@
|
|||||||
|
[DEFAULT]
|
||||||
|
|
||||||
|
remote_implementation = fake
|
||||||
|
|
||||||
|
# Show more verbose log output (sets INFO log level output)
|
||||||
|
verbose = True
|
||||||
|
|
||||||
|
# Show debugging output in logs (sets DEBUG log level output)
|
||||||
|
debug = True
|
||||||
|
|
||||||
|
# Address to bind the API server
|
||||||
|
bind_host = 0.0.0.0
|
||||||
|
|
||||||
|
# Port the bind the API server to
|
||||||
|
bind_port = 8779
|
||||||
|
|
||||||
|
# AMQP Connection info
|
||||||
|
rabbit_password=f7999d1955c5014aa32c
|
||||||
|
|
||||||
|
# SQLAlchemy connection string for the reference implementation
|
||||||
|
# registry server. Any valid SQLAlchemy connection string is fine.
|
||||||
|
# See: http://www.sqlalchemy.org/docs/05/reference/sqlalchemy/connections.html#sqlalchemy.create_engine
|
||||||
|
sql_connection = sqlite:///reddwarf_test.sqlite
|
||||||
|
#sql_connection = mysql://root:e1a2c042c828d3566d0a@localhost/reddwarf
|
||||||
|
#sql_connection = postgresql://reddwarf:reddwarf@localhost/reddwarf
|
||||||
|
|
||||||
|
# Period in seconds after which SQLAlchemy should reestablish its connection
|
||||||
|
# to the database.
|
||||||
|
#
|
||||||
|
# MySQL uses a default `wait_timeout` of 8 hours, after which it will drop
|
||||||
|
# idle connections. This can result in 'MySQL Gone Away' exceptions. If you
|
||||||
|
# notice this, you can lower this value to ensure that SQLAlchemy reconnects
|
||||||
|
# before MySQL can drop the connection.
|
||||||
|
sql_idle_timeout = 3600
|
||||||
|
|
||||||
|
#DB Api Implementation
|
||||||
|
db_api_implementation = "reddwarf.db.sqlalchemy.api"
|
||||||
|
|
||||||
|
# Path to the extensions
|
||||||
|
api_extensions_path = reddwarf/extensions
|
||||||
|
|
||||||
|
# Configuration options for talking to nova via the novaclient.
|
||||||
|
# These options are for an admin user in your keystone config.
|
||||||
|
# It proxy's the token received from the user to send to nova via this admin users creds,
|
||||||
|
# basically acting like the client via that proxy token.
|
||||||
|
reddwarf_proxy_admin_user = admin
|
||||||
|
reddwarf_proxy_admin_pass = 3de4922d8b6ac5a1aad9
|
||||||
|
reddwarf_proxy_admin_tenant_name = admin
|
||||||
|
reddwarf_auth_url = http://0.0.0.0:5000/v2.0
|
||||||
|
|
||||||
|
nova_region_name = RegionOne
|
||||||
|
nova_service_type = compute
|
||||||
|
nova_service_name = Compute Service
|
||||||
|
|
||||||
|
# ============ notifer queue kombu connection options ========================
|
||||||
|
|
||||||
|
notifier_queue_hostname = localhost
|
||||||
|
notifier_queue_userid = guest
|
||||||
|
notifier_queue_password = guest
|
||||||
|
notifier_queue_ssl = False
|
||||||
|
notifier_queue_port = 5672
|
||||||
|
notifier_queue_virtual_host = /
|
||||||
|
notifier_queue_transport = memory
|
||||||
|
|
||||||
|
[composite:reddwarf]
|
||||||
|
use = call:reddwarf.common.wsgi:versioned_urlmap
|
||||||
|
/: versions
|
||||||
|
/v0.1: reddwarfapi
|
||||||
|
|
||||||
|
[app:versions]
|
||||||
|
paste.app_factory = reddwarf.versions:app_factory
|
||||||
|
|
||||||
|
[pipeline:reddwarfapi]
|
||||||
|
pipeline = tokenauth authorization contextwrapper extensions reddwarfapp
|
||||||
|
#pipeline = debug extensions reddwarfapp
|
||||||
|
|
||||||
|
[filter:extensions]
|
||||||
|
paste.filter_factory = reddwarf.common.extensions:factory
|
||||||
|
|
||||||
|
[filter:tokenauth]
|
||||||
|
paste.filter_factory = keystone.middleware.auth_token:filter_factory
|
||||||
|
service_protocol = http
|
||||||
|
service_host = 127.0.0.1
|
||||||
|
service_port = 5000
|
||||||
|
auth_host = 127.0.0.1
|
||||||
|
auth_port = 35357
|
||||||
|
auth_protocol = http
|
||||||
|
auth_uri = http://127.0.0.1:5000/
|
||||||
|
admin_token = be19c524ddc92109a224
|
||||||
|
|
||||||
|
[filter:authorization]
|
||||||
|
paste.filter_factory = reddwarf.common.auth:AuthorizationMiddleware.factory
|
||||||
|
|
||||||
|
[filter:contextwrapper]
|
||||||
|
paste.filter_factory = reddwarf.common.wsgi:ContextMiddleware.factory
|
||||||
|
|
||||||
|
[app:reddwarfapp]
|
||||||
|
paste.app_factory = reddwarf.instance.service:app_factory
|
||||||
|
|
||||||
|
#Add this filter to log request and response for debugging
|
||||||
|
[filter:debug]
|
||||||
|
paste.filter_factory = reddwarf.common.wsgi:Debug
|
@ -35,13 +35,13 @@ class Config(object):
|
|||||||
@classmethod
|
@classmethod
|
||||||
def load_paste_app(cls, *args, **kwargs):
|
def load_paste_app(cls, *args, **kwargs):
|
||||||
conf, app = openstack_config.load_paste_app(*args, **kwargs)
|
conf, app = openstack_config.load_paste_app(*args, **kwargs)
|
||||||
cls.instance = conf
|
cls.instance.update(conf)
|
||||||
return conf, app
|
return conf, app
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def load_paste_config(cls, *args, **kwargs):
|
def load_paste_config(cls, *args, **kwargs):
|
||||||
conf_file, conf = openstack_config.load_paste_config(*args, **kwargs)
|
conf_file, conf = openstack_config.load_paste_config(*args, **kwargs)
|
||||||
cls.instance = conf
|
cls.instance.update(conf)
|
||||||
return conf
|
return conf
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
|
@ -17,10 +17,16 @@
|
|||||||
|
|
||||||
from reddwarf.common import config
|
from reddwarf.common import config
|
||||||
from novaclient.v1_1.client import Client
|
from novaclient.v1_1.client import Client
|
||||||
|
from reddwarf.guestagent.api import API
|
||||||
|
|
||||||
|
|
||||||
CONFIG = config.Config
|
CONFIG = config.Config
|
||||||
|
|
||||||
|
|
||||||
|
def create_guest_client(context, id):
|
||||||
|
return API(context, id)
|
||||||
|
|
||||||
|
|
||||||
def create_nova_client(context):
|
def create_nova_client(context):
|
||||||
# Quite annoying but due to a paste config loading bug.
|
# Quite annoying but due to a paste config loading bug.
|
||||||
# TODO(hub-cap): talk to the openstack-common people about this
|
# TODO(hub-cap): talk to the openstack-common people about this
|
||||||
@ -46,3 +52,17 @@ def create_nova_client(context):
|
|||||||
service_name=SERVICE_NAME)
|
service_name=SERVICE_NAME)
|
||||||
client.authenticate()
|
client.authenticate()
|
||||||
return client
|
return client
|
||||||
|
|
||||||
|
|
||||||
|
if CONFIG.get("remote_implementation", "real") == "fake":
|
||||||
|
# Override the functions above with fakes.
|
||||||
|
|
||||||
|
from reddwarf.tests.fakes.nova import fake_create_nova_client
|
||||||
|
from reddwarf.tests.fakes.guestagent import fake_create_guest_client
|
||||||
|
|
||||||
|
def create_guest_client(context, id):
|
||||||
|
return fake_create_guest_client(context, id)
|
||||||
|
|
||||||
|
def create_nova_client(context):
|
||||||
|
return fake_create_nova_client(context)
|
||||||
|
|
||||||
|
@ -34,111 +34,110 @@ LOG = logging.getLogger(__name__)
|
|||||||
class API(object):
|
class API(object):
|
||||||
"""API for interacting with the guest manager."""
|
"""API for interacting with the guest manager."""
|
||||||
|
|
||||||
def __init__(self, **kwargs):
|
def __init__(self, context, id):
|
||||||
super(API, self).__init__(**kwargs)
|
self.context = context
|
||||||
|
self.id = id
|
||||||
|
|
||||||
def _get_routing_key(self, context, id):
|
def _get_routing_key(self):
|
||||||
"""Create the routing key based on the container id"""
|
"""Create the routing key based on the container id"""
|
||||||
return "guestagent.%s" % id
|
return "guestagent.%s" % self.id
|
||||||
|
|
||||||
def create_user(self, context, id, users):
|
def create_user(self, users):
|
||||||
"""Make an asynchronous call to create a new database user"""
|
"""Make an asynchronous call to create a new database user"""
|
||||||
LOG.debug("Creating Users for Instance %s", id)
|
LOG.debug("Creating Users for Instance %s", self.id)
|
||||||
rpc.cast(context, self._get_routing_key(context, id),
|
rpc.cast(self.context, self._get_routing_key(),
|
||||||
{"method": "create_user",
|
{"method": "create_user",
|
||||||
"args": {"users": users}
|
"args": {"users": users}
|
||||||
})
|
})
|
||||||
|
|
||||||
def list_users(self, context, id):
|
def list_users(self):
|
||||||
"""Make an asynchronous call to list database users"""
|
"""Make an asynchronous call to list database users"""
|
||||||
LOG.debug("Listing Users for Instance %s", id)
|
LOG.debug("Listing Users for Instance %s", self.id)
|
||||||
return rpc.call(context, self._get_routing_key(context, id),
|
return rpc.call(context, self._get_routing_key(),
|
||||||
{"method": "list_users"})
|
{"method": "list_users"})
|
||||||
|
|
||||||
def delete_user(self, context, id, 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"""
|
||||||
LOG.debug("Deleting user %s for Instance %s",
|
LOG.debug("Deleting user %s for Instance %s", user, self.id)
|
||||||
user, id)
|
rpc.cast(self.context, self._get_routing_key(),
|
||||||
rpc.cast(context, self._get_routing_key(context, id),
|
|
||||||
{"method": "delete_user",
|
{"method": "delete_user",
|
||||||
"args": {"user": user}
|
"args": {"user": user}
|
||||||
})
|
})
|
||||||
|
|
||||||
def create_database(self, context, id, databases):
|
def create_database(self, databases):
|
||||||
"""Make an asynchronous call to create a new database
|
"""Make an asynchronous call to create a new database
|
||||||
within the specified container"""
|
within the specified container"""
|
||||||
LOG.debug("Creating databases for Instance %s", id)
|
LOG.debug("Creating databases for Instance %s", self.id)
|
||||||
rpc.cast(context, self._get_routing_key(context, id),
|
rpc.cast(self.context, self._get_routing_key(),
|
||||||
{"method": "create_database",
|
{"method": "create_database",
|
||||||
"args": {"databases": databases}
|
"args": {"databases": databases}
|
||||||
})
|
})
|
||||||
|
|
||||||
def list_databases(self, context, id):
|
def list_databases(self):
|
||||||
"""Make an asynchronous call to list database users"""
|
"""Make an asynchronous call to list database users"""
|
||||||
LOG.debug("Listing Users for Instance %s", id)
|
LOG.debug("Listing Users for Instance %s", self.id)
|
||||||
return rpc.call(context, self._get_routing_key(context, id),
|
return rpc.call(self.context, self._get_routing_key(),
|
||||||
{"method": "list_databases"})
|
{"method": "list_databases"})
|
||||||
|
|
||||||
def delete_database(self, context, id, database):
|
def delete_database(self, database):
|
||||||
"""Make an asynchronous call to delete an existing database
|
"""Make an asynchronous call to delete an existing database
|
||||||
within the specified container"""
|
within the specified container"""
|
||||||
LOG.debug("Deleting database %s for Instance %s",
|
LOG.debug("Deleting database %s for Instance %s", database, self.id)
|
||||||
database, id)
|
rpc.cast(self.context, self._get_routing_key(),
|
||||||
rpc.cast(context, self._get_routing_key(context, id),
|
|
||||||
{"method": "delete_database",
|
{"method": "delete_database",
|
||||||
"args": {"database": database}
|
"args": {"database": database}
|
||||||
})
|
})
|
||||||
|
|
||||||
def enable_root(self, context, id):
|
def enable_root(self):
|
||||||
"""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", id)
|
LOG.debug("Enable root user for Instance %s", self.id)
|
||||||
return rpc.call(context, self._get_routing_key(context, id),
|
return rpc.call(self.context, self._get_routing_key(),
|
||||||
{"method": "enable_root"})
|
{"method": "enable_root"})
|
||||||
|
|
||||||
def disable_root(self, context, id):
|
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", id)
|
LOG.debug("Disable root user for Instance %s", self.id)
|
||||||
return rpc.call(context, self._get_routing_key(context, id),
|
return rpc.call(self.context, self._get_routing_key(),
|
||||||
{"method": "disable_root"})
|
{"method": "disable_root"})
|
||||||
|
|
||||||
def is_root_enabled(self, context, id):
|
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", id)
|
LOG.debug("Check root access for Instance %s", self.id)
|
||||||
return rpc.call(context, self._get_routing_key(context, id),
|
return rpc.call(self.context, self._get_routing_key(),
|
||||||
{"method": "is_root_enabled"})
|
{"method": "is_root_enabled"})
|
||||||
|
|
||||||
def get_diagnostics(self, context, id):
|
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", id)
|
LOG.debug("Check diagnostics on Instance %s", self.id)
|
||||||
return rpc.call(context, self._get_routing_key(context, id),
|
return rpc.call(self.context, self._get_routing_key(),
|
||||||
{"method": "get_diagnostics"})
|
{"method": "get_diagnostics"})
|
||||||
|
|
||||||
def prepare(self, context, id, memory_mb, databases):
|
def prepare(self, memory_mb, databases):
|
||||||
"""Make an asynchronous call to prepare the guest
|
"""Make an asynchronous call to prepare the guest
|
||||||
as a database container"""
|
as a database container"""
|
||||||
LOG.debug(_("Sending the call to prepare the Guest"))
|
LOG.debug(_("Sending the call to prepare the Guest"))
|
||||||
rpc.cast_with_consumer(context, self._get_routing_key(context, id),
|
rpc.cast_with_consumer(self.context, self._get_routing_key(),
|
||||||
{"method": "prepare",
|
{"method": "prepare",
|
||||||
"args": {"databases": databases,
|
"args": {"databases": databases,
|
||||||
"memory_mb": memory_mb}
|
"memory_mb": memory_mb}
|
||||||
})
|
})
|
||||||
|
|
||||||
def restart(self, context, id):
|
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."))
|
||||||
rpc.call(context, self._get_routing_key(context, id),
|
rpc.call(self.context, self._get_routing_key(),
|
||||||
{"method": "restart",
|
{"method": "restart",
|
||||||
"args": {}
|
"args": {}
|
||||||
})
|
})
|
||||||
|
|
||||||
def start_mysql_with_conf_changes(self, context, id, 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."))
|
||||||
try:
|
try:
|
||||||
rpc.call(context, self._get_routing_key(context, id),
|
rpc.call(self.context, self._get_routing_key(),
|
||||||
{"method": "start_mysql_with_conf_changes",
|
{"method": "start_mysql_with_conf_changes",
|
||||||
"args": {'updated_memory_size': updated_memory_size}
|
"args": {'updated_memory_size': updated_memory_size}
|
||||||
})
|
})
|
||||||
@ -146,11 +145,11 @@ class API(object):
|
|||||||
LOG.error(e)
|
LOG.error(e)
|
||||||
raise exception.GuestError(original_message=str(e))
|
raise exception.GuestError(original_message=str(e))
|
||||||
|
|
||||||
def stop_mysql(self, context, id):
|
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."))
|
||||||
try:
|
try:
|
||||||
rpc.call(context, self._get_routing_key(context, id),
|
rpc.call(self.context, self._get_routing_key(),
|
||||||
{"method": "stop_mysql",
|
{"method": "stop_mysql",
|
||||||
"args": {}
|
"args": {}
|
||||||
})
|
})
|
||||||
@ -158,8 +157,8 @@ class API(object):
|
|||||||
LOG.error(e)
|
LOG.error(e)
|
||||||
raise exception.GuestError(original_message=str(e))
|
raise exception.GuestError(original_message=str(e))
|
||||||
|
|
||||||
def upgrade(self, context, id):
|
def upgrade(self):
|
||||||
"""Make an asynchronous call to self upgrade the guest agent"""
|
"""Make an asynchronous call to self upgrade the guest agent"""
|
||||||
topic = self._get_routing_key(context, id)
|
topic = self._get_routing_key()
|
||||||
LOG.debug("Sending an upgrade call to nova-guest %s", topic)
|
LOG.debug("Sending an upgrade call to nova-guest %s", topic)
|
||||||
rpc.cast_with_consumer(context, topic, {"method": "upgrade"})
|
rpc.cast_with_consumer(self.context, topic, {"method": "upgrade"})
|
||||||
|
@ -33,6 +33,8 @@ from reddwarf.common.models import ModelBase
|
|||||||
from novaclient import exceptions as nova_exceptions
|
from novaclient import exceptions as nova_exceptions
|
||||||
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
|
||||||
|
from reddwarf.common.remote import create_guest_client
|
||||||
|
|
||||||
|
|
||||||
CONFIG = config.Config
|
CONFIG = config.Config
|
||||||
LOG = logging.getLogger(__name__)
|
LOG = logging.getLogger(__name__)
|
||||||
@ -79,19 +81,19 @@ class Instance(object):
|
|||||||
self.service_status = service_status
|
self.service_status = service_status
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def load(context, uuid):
|
def load(context, id):
|
||||||
if context is None:
|
if context is None:
|
||||||
raise TypeError("Argument context not defined.")
|
raise TypeError("Argument context not defined.")
|
||||||
elif uuid is None:
|
elif id is None:
|
||||||
raise TypeError("Argument uuid not defined.")
|
raise TypeError("Argument id not defined.")
|
||||||
client = create_nova_client(context)
|
|
||||||
try:
|
try:
|
||||||
db_info = DBInstance.find_by(id=uuid)
|
db_info = DBInstance.find_by(id=id)
|
||||||
except rd_exceptions.NotFound:
|
except rd_exceptions.NotFound:
|
||||||
raise rd_exceptions.NotFound(uuid=uuid)
|
raise rd_exceptions.NotFound(uuid=id)
|
||||||
server = load_server(context, db_info.id, db_info.compute_instance_id)
|
server = load_server(context, db_info.id, db_info.compute_instance_id)
|
||||||
task_status = db_info.task_status
|
task_status = db_info.task_status
|
||||||
service_status = InstanceServiceStatus.find_by(instance_id=uuid)
|
service_status = InstanceServiceStatus.find_by(instance_id=id)
|
||||||
|
LOG.info("service status=%s" % service_status)
|
||||||
return Instance(context, db_info, server, service_status)
|
return Instance(context, db_info, server, service_status)
|
||||||
|
|
||||||
def delete(self, force=False):
|
def delete(self, force=False):
|
||||||
@ -130,7 +132,8 @@ class Instance(object):
|
|||||||
service_status = InstanceServiceStatus.create(instance_id=db_info.id,
|
service_status = InstanceServiceStatus.create(instance_id=db_info.id,
|
||||||
status=ServiceStatuses.NEW)
|
status=ServiceStatuses.NEW)
|
||||||
# Now wait for the response from the create to do additional work
|
# Now wait for the response from the create to do additional work
|
||||||
guest_api.API().prepare(context, db_info.id, 512, [])
|
guest = create_guest_client(context, db_info.id)
|
||||||
|
guest.prepare(512, [])
|
||||||
return Instance(context, db_info, server, service_status)
|
return Instance(context, db_info, server, service_status)
|
||||||
|
|
||||||
@property
|
@property
|
||||||
@ -156,7 +159,7 @@ class Instance(object):
|
|||||||
# timeouts determine if the task_status should be integrated here
|
# timeouts determine if the task_status should be integrated here
|
||||||
# or removed entirely.
|
# or removed entirely.
|
||||||
# If the server is in any of these states they take precedence.
|
# If the server is in any of these states they take precedence.
|
||||||
if self.server.status in ["ERROR", "REBOOT", "RESIZE"]:
|
if self.server.status in ["BUILD", "ERROR", "REBOOT", "RESIZE"]:
|
||||||
return self.server.status
|
return self.server.status
|
||||||
# The service is only paused during a reboot.
|
# The service is only paused during a reboot.
|
||||||
if ServiceStatuses.PAUSED == self.service_status.status:
|
if ServiceStatuses.PAUSED == self.service_status.status:
|
||||||
|
@ -112,7 +112,7 @@ class InstanceController(BaseController):
|
|||||||
context = req.environ[wsgi.CONTEXT_KEY]
|
context = req.environ[wsgi.CONTEXT_KEY]
|
||||||
try:
|
try:
|
||||||
# TODO(hub-cap): start testing the failure cases here
|
# TODO(hub-cap): start testing the failure cases here
|
||||||
server = models.Instance.load(context=context, uuid=id)
|
server = models.Instance.load(context=context, id=id)
|
||||||
except exception.ReddwarfError, e:
|
except exception.ReddwarfError, e:
|
||||||
# TODO(hub-cap): come up with a better way than
|
# TODO(hub-cap): come up with a better way than
|
||||||
# this to get the message
|
# this to get the message
|
||||||
@ -130,7 +130,7 @@ class InstanceController(BaseController):
|
|||||||
context = req.environ[wsgi.CONTEXT_KEY]
|
context = req.environ[wsgi.CONTEXT_KEY]
|
||||||
try:
|
try:
|
||||||
# TODO(hub-cap): start testing the failure cases here
|
# TODO(hub-cap): start testing the failure cases here
|
||||||
instance = models.Instance.load(context=context, uuid=id)
|
instance = models.Instance.load(context=context, id=id)
|
||||||
except exception.ReddwarfError, e:
|
except exception.ReddwarfError, e:
|
||||||
# TODO(hub-cap): come up with a better way than
|
# TODO(hub-cap): come up with a better way than
|
||||||
# this to get the message
|
# this to get the message
|
||||||
@ -166,7 +166,6 @@ class InstanceController(BaseController):
|
|||||||
flavor_ref = body['instance']['flavorRef']
|
flavor_ref = body['instance']['flavorRef']
|
||||||
instance = models.Instance.create(context, name, flavor_ref, image_id)
|
instance = models.Instance.create(context, name, flavor_ref, image_id)
|
||||||
|
|
||||||
#TODO(cp16net): need to set the return code correctly
|
|
||||||
return wsgi.Result(views.InstanceDetailView(instance).data(), 200)
|
return wsgi.Result(views.InstanceDetailView(instance).data(), 200)
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
|
21
reddwarf/tests/fakes/__init__.py
Normal file
21
reddwarf/tests/fakes/__init__.py
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
# vim: tabstop=4 shiftwidth=4 softtabstop=4
|
||||||
|
|
||||||
|
# Copyright 2011 OpenStack LLC.
|
||||||
|
# All Rights Reserved.
|
||||||
|
#
|
||||||
|
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||||
|
# not use this file except in compliance with the License. You may obtain
|
||||||
|
# a copy of the License at
|
||||||
|
#
|
||||||
|
# http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
#
|
||||||
|
# Unless required by applicable law or agreed to in writing, software
|
||||||
|
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||||
|
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||||
|
# License for the specific language governing permissions and limitations
|
||||||
|
# under the License.
|
||||||
|
|
||||||
|
"""
|
||||||
|
Implements a fake version of the models code so that the server can be stood
|
||||||
|
up and run under test quickly.
|
||||||
|
"""
|
39
reddwarf/tests/fakes/common.py
Normal file
39
reddwarf/tests/fakes/common.py
Normal file
@ -0,0 +1,39 @@
|
|||||||
|
# vim: tabstop=4 shiftwidth=4 softtabstop=4
|
||||||
|
|
||||||
|
# Copyright 2010-2011 OpenStack LLC.
|
||||||
|
# All Rights Reserved.
|
||||||
|
#
|
||||||
|
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||||
|
# not use this file except in compliance with the License. You may obtain
|
||||||
|
# a copy of the License at
|
||||||
|
#
|
||||||
|
# http: //www.apache.org/licenses/LICENSE-2.0
|
||||||
|
#
|
||||||
|
# Unless required by applicable law or agreed to in writing, software
|
||||||
|
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||||
|
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||||
|
# License for the specific language governing permissions and limitations
|
||||||
|
# under the License.
|
||||||
|
|
||||||
|
"""Common code to help in faking the models."""
|
||||||
|
|
||||||
|
import time
|
||||||
|
import stubout
|
||||||
|
|
||||||
|
|
||||||
|
class EventSimulator(object):
|
||||||
|
"""Simulates a resource that changes over time.
|
||||||
|
|
||||||
|
Has a list of events which execute in real time to change state.
|
||||||
|
The implementation is very dumb; if you give it two events at once the
|
||||||
|
last one wins.
|
||||||
|
|
||||||
|
"""
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def add_event(time_from_now_in_seconds, func):
|
||||||
|
if time_from_now_in_seconds <= 0:
|
||||||
|
func()
|
||||||
|
else:
|
||||||
|
import eventlet
|
||||||
|
eventlet.spawn_after(time_from_now_in_seconds, func)
|
50
reddwarf/tests/fakes/guestagent.py
Normal file
50
reddwarf/tests/fakes/guestagent.py
Normal file
@ -0,0 +1,50 @@
|
|||||||
|
# vim: tabstop=4 shiftwidth=4 softtabstop=4
|
||||||
|
|
||||||
|
# Copyright 2010-2012 OpenStack LLC.
|
||||||
|
# All Rights Reserved.
|
||||||
|
#
|
||||||
|
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||||
|
# not use this file except in compliance with the License. You may obtain
|
||||||
|
# a copy of the License at
|
||||||
|
#
|
||||||
|
# http: //www.apache.org/licenses/LICENSE-2.0
|
||||||
|
#
|
||||||
|
# Unless required by applicable law or agreed to in writing, software
|
||||||
|
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||||
|
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||||
|
# License for the specific language governing permissions and limitations
|
||||||
|
# under the License.
|
||||||
|
|
||||||
|
import logging
|
||||||
|
|
||||||
|
|
||||||
|
from reddwarf.tests.fakes.common import EventSimulator
|
||||||
|
|
||||||
|
|
||||||
|
DB = {}
|
||||||
|
LOG = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
|
class FakeGuest(object):
|
||||||
|
|
||||||
|
def __init__(self, id):
|
||||||
|
self.id = id
|
||||||
|
|
||||||
|
def prepare(self, memory_mb, databases):
|
||||||
|
from reddwarf.instance.models import InstanceServiceStatus
|
||||||
|
from reddwarf.instance.models import ServiceStatuses
|
||||||
|
def update_db():
|
||||||
|
status = InstanceServiceStatus.find_by(instance_id=self.id)
|
||||||
|
status.status = ServiceStatuses.RUNNING
|
||||||
|
status.save()
|
||||||
|
EventSimulator.add_event(2.0, update_db)
|
||||||
|
|
||||||
|
|
||||||
|
def get_or_create(id):
|
||||||
|
if id not in DB:
|
||||||
|
DB[id] = FakeGuest(id)
|
||||||
|
return DB[id]
|
||||||
|
|
||||||
|
|
||||||
|
def fake_create_guest_client(context, id):
|
||||||
|
return get_or_create(id)
|
176
reddwarf/tests/fakes/nova.py
Normal file
176
reddwarf/tests/fakes/nova.py
Normal file
@ -0,0 +1,176 @@
|
|||||||
|
# vim: tabstop=4 shiftwidth=4 softtabstop=4
|
||||||
|
|
||||||
|
# Copyright 2010-2012 OpenStack LLC.
|
||||||
|
# All Rights Reserved.
|
||||||
|
#
|
||||||
|
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||||
|
# not use this file except in compliance with the License. You may obtain
|
||||||
|
# a copy of the License at
|
||||||
|
#
|
||||||
|
# http: //www.apache.org/licenses/LICENSE-2.0
|
||||||
|
#
|
||||||
|
# Unless required by applicable law or agreed to in writing, software
|
||||||
|
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||||
|
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||||
|
# License for the specific language governing permissions and limitations
|
||||||
|
# under the License.
|
||||||
|
|
||||||
|
import logging
|
||||||
|
from novaclient.v1_1.client import Client
|
||||||
|
from novaclient import exceptions as nova_exceptions
|
||||||
|
import time
|
||||||
|
from reddwarf.tests.fakes.common import EventSimulator
|
||||||
|
|
||||||
|
|
||||||
|
LOG = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
|
class FakeFlavor(object):
|
||||||
|
|
||||||
|
def __init__(self, id, disk, name, ram):
|
||||||
|
self.id = id
|
||||||
|
self.disk = disk
|
||||||
|
self.name = name
|
||||||
|
self.ram = ram
|
||||||
|
|
||||||
|
@property
|
||||||
|
def links(self):
|
||||||
|
return [{
|
||||||
|
"href": "http://localhost:8774/v2/"
|
||||||
|
"5064d71eb09c47e1956cf579822bae9a/flavors/%s" % self.id,
|
||||||
|
"rel": link_type
|
||||||
|
} for link_type in ['self', 'bookmark']]
|
||||||
|
|
||||||
|
@property
|
||||||
|
def href_suffix(self):
|
||||||
|
return "flavors/%s" % self.id
|
||||||
|
|
||||||
|
def to_dict(self):
|
||||||
|
return {"id":self.id, "links":self.links}
|
||||||
|
|
||||||
|
class FakeFlavors(object):
|
||||||
|
|
||||||
|
def __init__(self):
|
||||||
|
self.db = {}
|
||||||
|
self._add(1, 0, "m1.tiny", 512)
|
||||||
|
self._add(2, 10, "m1.small", 2048)
|
||||||
|
self._add(3, 10, "m1.medium", 4096)
|
||||||
|
self._add(4, 10, "m1.large", 8192)
|
||||||
|
self._add(5, 10, "m1.xlarge", 16384)
|
||||||
|
|
||||||
|
def _add(self, *args, **kwargs):
|
||||||
|
new_flavor = FakeFlavor(*args, **kwargs)
|
||||||
|
self.db[new_flavor.id] = new_flavor
|
||||||
|
|
||||||
|
def get(self, id):
|
||||||
|
if id not in self.db:
|
||||||
|
raise nova_exceptions.NotFound(404, "Flavor id not found %s" % id)
|
||||||
|
return self.db[id]
|
||||||
|
|
||||||
|
def get_by_href(self, href):
|
||||||
|
for id in self.db:
|
||||||
|
value = self.db[id]
|
||||||
|
# Use inexact match since faking the exact endpoints would be
|
||||||
|
# difficult.
|
||||||
|
if href.endswith(value.href_suffix):
|
||||||
|
return value
|
||||||
|
raise nova_exceptions.NotFound(404, "Flavor href not found %s" % href)
|
||||||
|
|
||||||
|
class FakeServer(object):
|
||||||
|
|
||||||
|
def __init__(self, parent, id, name, image_id, flavor_ref):
|
||||||
|
self.id = id
|
||||||
|
self.parent = parent
|
||||||
|
self.name = name
|
||||||
|
self.image_id = image_id
|
||||||
|
self.flavor_ref = flavor_ref
|
||||||
|
self.events = EventSimulator()
|
||||||
|
self.schedule_status("BUILD", 0.0)
|
||||||
|
|
||||||
|
@property
|
||||||
|
def addresses(self):
|
||||||
|
return ["We don't even use this."]
|
||||||
|
|
||||||
|
def delete(self):
|
||||||
|
self.schedule_status = []
|
||||||
|
self._current_status = "SHUTDOWN"
|
||||||
|
self.parent.schedule_delete(self.id, 1.5)
|
||||||
|
|
||||||
|
@property
|
||||||
|
def flavor(self):
|
||||||
|
return FLAVORS.get_by_href(self.flavor_ref).to_dict()
|
||||||
|
|
||||||
|
@property
|
||||||
|
def links(self):
|
||||||
|
return [{
|
||||||
|
"href": "https://localhost:9999/v1.0/1234/instances/%s" % self.id,
|
||||||
|
"rel": link_type
|
||||||
|
} for link_type in ['self', 'bookmark']]
|
||||||
|
|
||||||
|
def schedule_status(self, new_status, time_from_now):
|
||||||
|
"""Makes a new status take effect at the given time."""
|
||||||
|
def set_status():
|
||||||
|
self._current_status = new_status
|
||||||
|
self.events.add_event(time_from_now, set_status)
|
||||||
|
|
||||||
|
@property
|
||||||
|
def status(self):
|
||||||
|
return self._current_status
|
||||||
|
|
||||||
|
@property
|
||||||
|
def created(self):
|
||||||
|
return "2012-01-25T21:55:51Z"
|
||||||
|
|
||||||
|
@property
|
||||||
|
def updated(self):
|
||||||
|
return "2012-01-25T21:55:51Z"
|
||||||
|
|
||||||
|
|
||||||
|
class FakeServers(object):
|
||||||
|
|
||||||
|
def __init__(self, flavors):
|
||||||
|
self.db = {}
|
||||||
|
self.flavors = flavors
|
||||||
|
self.next_id = 10;
|
||||||
|
self.events = EventSimulator()
|
||||||
|
|
||||||
|
def create(self, name, image_id, flavor_ref, files):
|
||||||
|
id = "FAKE_%d" % self.next_id
|
||||||
|
self.next_id += 1
|
||||||
|
server = FakeServer(self, id, name, image_id, flavor_ref)
|
||||||
|
self.db[id] = server
|
||||||
|
server.schedule_status("ACTIVE", 1)
|
||||||
|
return server
|
||||||
|
|
||||||
|
def get(self, id):
|
||||||
|
if id not in self.db:
|
||||||
|
LOG.error("Couldn't find id %s, collection=%s" % (id, self.db))
|
||||||
|
raise nova_exceptions.NotFound(404, "Not found")
|
||||||
|
else:
|
||||||
|
return self.db[id]
|
||||||
|
|
||||||
|
def list(self):
|
||||||
|
return [v for (k, v) in self.db.items()]
|
||||||
|
|
||||||
|
def schedule_delete(self, id, time_from_now):
|
||||||
|
def delete_server():
|
||||||
|
LOG.info("Simulated event ended, deleting server %s." % id)
|
||||||
|
del self.db[id]
|
||||||
|
self.events.add_event(time_from_now, delete_server)
|
||||||
|
|
||||||
|
|
||||||
|
# The global var contains the servers dictionary in use for the life of these
|
||||||
|
# tests.
|
||||||
|
FLAVORS = FakeFlavors()
|
||||||
|
SERVERS = FakeServers(FLAVORS)
|
||||||
|
|
||||||
|
|
||||||
|
class FakeClient(object):
|
||||||
|
|
||||||
|
def __init__(self):
|
||||||
|
self.servers = SERVERS
|
||||||
|
self.flavors = FLAVORS
|
||||||
|
|
||||||
|
|
||||||
|
def fake_create_nova_client(*args, **kwargs):
|
||||||
|
return FakeClient()
|
Loading…
x
Reference in New Issue
Block a user