diff --git a/config/synergy_scheduler.conf b/config/synergy_scheduler.conf index e1878cf..c0e53a8 100644 --- a/config/synergy_scheduler.conf +++ b/config/synergy_scheduler.conf @@ -11,12 +11,6 @@ rate = 1 # this allows Synergy to not check the whole queue when looking for VMs to start backfill_depth = 100 -# set the notification topic used by Nova for informing listeners about the state -# changes of the VMs. In case some other service (e.g. Ceilometer) is listening -# on the default Nova topic (i.e. "notifications"), please define a new topic -specific for Synergy (e.g. notification_topics = notifications,synergy_notifications) -notification_topic = notifications - [FairShareManager] autostart = True @@ -139,7 +133,13 @@ conductor_topic = conductor compute_topic = compute # set the Nova scheduler topic (default: scheduler) + scheduler_topic = scheduler +# set the notification topic used by Nova for informing listeners about the state +# changes of the VMs. In case some other service (e.g. Ceilometer) is listening +# on the default Nova topic (i.e. "notifications"), please define a new topic +specific for Synergy (e.g. notification_topics = notifications,synergy_notifications) +notification_topic = notification # set the Nova database connection db_connection=DIALECT+DRIVER://USER:PASSWORD@DB_HOST/nova diff --git a/synergy_scheduler_manager/common/request.py b/synergy_scheduler_manager/common/request.py index ac17792..9597bd0 100644 --- a/synergy_scheduler_manager/common/request.py +++ b/synergy_scheduler_manager/common/request.py @@ -26,160 +26,122 @@ permissions and limitations under the License.""" class Request(object): def __init__(self): + self.id = None + self.user_id = None + self.project_id = None + self.action = None + self.data = None self.context = None - self.instance = None - self.image = None - self.filter_properties = None - self.admin_password = None - self.injected_files = None - self.requested_networks = None - self.security_groups = None - self.block_device_mapping = None - self.legacy_bdm = None + self.server = None + self.retry = None + self.created_at = None - def getAdminPassword(self): - return self.admin_password - - def getId(self): - if self.instance: - return self.instance["nova_object.data"]["uuid"] - - return None - - def getInstance(self): - return self.instance - - def getServer(self): - server = None - - if self.instance: - instance_data = self.instance["nova_object.data"] - flavor_data = instance_data["flavor"]["nova_object.data"] - - flavor = Flavor() - flavor.setId(flavor_data["flavorid"]) - flavor.setName(flavor_data["name"]) - flavor.setMemory(flavor_data["memory_mb"]) - flavor.setVCPUs(flavor_data["vcpus"]) - flavor.setStorage(flavor_data["root_gb"]) - - server = Server() - server.setFlavor(flavor) - server.setId(instance_data["uuid"]) - server.setUserId(instance_data["user_id"]) - server.setProjectId(instance_data["project_id"]) - server.setCreatedAt(instance_data["created_at"]) - server.setMetadata(instance_data["metadata"]) - server.setKeyName(instance_data["key_name"]) - - if "user_data" in instance_data: - user_data = instance_data["user_data"] - if user_data: - server.setUserData(utils.decodeBase64(user_data)) - - return server - - def getImage(self): - return self.image - - def getUserId(self): - if self.instance: - return self.instance["nova_object.data"]["user_id"] - - return None - - def getProjectId(self): - if self.instance: - return self.instance["nova_object.data"]["project_id"] - - return None + def getAction(self): + return self.action def getContext(self): return self.context def getCreatedAt(self): - if self.instance: - created_at = self.instance["nova_object.data"]["created_at"] - timestamp = datetime.strptime(created_at, "%Y-%m-%dT%H:%M:%SZ") - return timestamp + return self.created_at - return 0 + def getData(self): + return self.data - def getMetadata(self): - if self.instance: - return self.instance["nova_object.data"]["metadata"] + def getId(self): + return self.id - return None + def getServer(self): + return self.server + + def getUserId(self): + return self.user_id + + def getProjectId(self): + return self.project_id def getRetry(self): - if self.filter_properties: - return self.filter_properties.get("retry", None) - - return None - - def getFilterProperties(self): - return self.filter_properties - - def getInjectedFiles(self): - return self.injected_files - - def getRequestedNetworks(self): - return self.requested_networks - - def getSecurityGroups(self): - return self.security_groups - - def getBlockDeviceMapping(self): - return self.block_device_mapping - - def getLegacyBDM(self): - return self.legacy_bdm + return self.retry def toDict(self): request = {} + request['action'] = self.action request['context'] = self.context - request['instance'] = self.instance - request['image'] = self.image - request['filter_properties'] = self.filter_properties - request['admin_password'] = self.admin_password - request['injected_files'] = self.injected_files - request['requested_networks'] = self.requested_networks - request['security_groups'] = self.security_groups - request['block_device_mapping'] = self.block_device_mapping - request['legacy_bdm'] = self.legacy_bdm + request['data'] = self.data return request @classmethod def fromDict(cls, request_dict): request = Request() - request.context = request_dict['context'] - request.instance = request_dict['instance'] - request.image = request_dict['image'] - request.filter_properties = request_dict['filter_properties'] - request.admin_password = request_dict['admin_password'] - request.injected_files = request_dict['injected_files'] - request.requested_networks = request_dict['requested_networks'] - request.security_groups = request_dict['security_groups'] - request.block_device_mapping = request_dict['block_device_mapping'] - request.legacy_bdm = request_dict['legacy_bdm'] - - return request - - @classmethod - def build(cls, context, instance, image, filter_properties, - admin_password, injected_files, requested_networks, - security_groups, block_device_mapping=None, legacy_bdm=True): - request = Request() - request.context = context - request.instance = instance - request.image = image - request.filter_properties = filter_properties - request.admin_password = admin_password - request.injected_files = injected_files - request.requested_networks = requested_networks - request.security_groups = security_groups - request.block_device_mapping = block_device_mapping - request.legacy_bdm = legacy_bdm + request.data = request_dict["data"] + request.action = request_dict["action"] + request.context = request_dict["context"] + + if "instances" in request.data: + instance = request.data["instances"][0] + else: + build_request = request.data["build_requests"][0] + instance = build_request["nova_object.data"]["instance"] + + instance_data = instance["nova_object.data"] + + request.id = instance_data["uuid"] + request.user_id = instance_data["user_id"] + request.project_id = instance_data["project_id"] + + created_at = instance_data["created_at"] + request.created_at = datetime.strptime(created_at, + "%Y-%m-%dT%H:%M:%SZ") + + flavor_data = instance_data["flavor"]["nova_object.data"] + flavor = Flavor() + flavor.setId(flavor_data["flavorid"]) + flavor.setName(flavor_data["name"]) + flavor.setMemory(flavor_data["memory_mb"]) + flavor.setVCPUs(flavor_data["vcpus"]) + flavor.setStorage(flavor_data["root_gb"]) + + server = Server() + server.setFlavor(flavor) + server.setId(instance_data["uuid"]) + server.setUserId(instance_data["user_id"]) + server.setProjectId(instance_data["project_id"]) + server.setCreatedAt(instance_data["created_at"]) + server.setMetadata(instance_data["metadata"]) + server.setKeyName(instance_data["key_name"]) + + user_data = instance_data.get("user_data", None) + if user_data: + try: + data = utils.decodeBase64(user_data) + quota = utils.getConfigParameter(data, "quota", "synergy") + if not quota: + quota = utils.getConfigParameter(data, "quota") + + metadata = instance_data.get("metadata", {}) + + if quota is None or quota == "private" or quota != "shared": + server.setType("permanent") + metadata["quota"] = "private" + + elif quota == "shared": + server.setType("ephemeral") + metadata["quota"] = "shared" + except Exception: + server.setType("permanent") + metadata["quota"] = "private" + request.server = server + + if "filter_properties" in request.data: + filter_properties = request.data["filter_properties"] + request.retry = filter_properties["retry"] + else: + request_spec = request.data["request_specs"][0] + nova_object = request_spec["nova_object.data"] + request.retry = nova_object["retry"] + + if not request.retry: + request.retry = {} return request diff --git a/synergy_scheduler_manager/nova_manager.py b/synergy_scheduler_manager/nova_manager.py index 1754ab1..c4e318e 100644 --- a/synergy_scheduler_manager/nova_manager.py +++ b/synergy_scheduler_manager/nova_manager.py @@ -42,18 +42,101 @@ LOG = logging.getLogger(__name__) CONF = cfg.CONF +class ServerEventHandler(object): + + def __init__(self, nova_manager): + super(ServerEventHandler, self).__init__() + + self.nova_manager = nova_manager + + def _makeServer(self, server_info): + if not server_info: + return + + flavor = Flavor() + flavor.setMemory(server_info["memory_mb"]) + flavor.setVCPUs(server_info["vcpus"]) + flavor.setStorage(server_info["root_gb"]) + + if "instance_type" in server_info: + flavor.setName(server_info["instance_type"]) + + server = Server() + server.setFlavor(flavor) + server.setUserId(server_info["user_id"]) + server.setMetadata(server_info["metadata"]) + server.setDeletedAt(server_info["deleted_at"]) + server.setTerminatedAt(server_info["terminated_at"]) + + if "host" in server_info: + server.setHost(server_info["host"]) + + if "uuid" in server_info: + server.setId(server_info["uuid"]) + elif "instance_id" in server_info: + server.setId(server_info["instance_id"]) + + if "project_id" in server_info: + server.setProjectId(server_info["project_id"]) + elif "tenant_id" in server_info: + server.setProjectId(server_info["tenant_id"]) + + if "vm_state" in server_info: + server.setState(server_info["vm_state"]) + elif "state" in server_info: + server.setState(server_info["state"]) + + return server + + def info(self, ctxt, publisher_id, event_type, payload, metadata): + LOG.debug("Notification INFO: event_type=%s payload=%s" + % (event_type, payload)) + + if payload is None or "state" not in payload: + return + + state = payload["state"] + + event_types = ["compute.instance.create.end", + "compute.instance.delete.end", + "compute.instance.update", + "scheduler.run_instance"] + + if event_type not in event_types: + return + + server_info = None + + if event_type == "scheduler.run_instance": + server_info = payload["request_spec"]["instance_type"] + else: + server_info = payload + + server = self._makeServer(server_info) + + self.nova_manager.notify(event_type="SERVER_EVENT", server=server, + event=event_type, state=state) + + def warn(self, ctxt, publisher_id, event_type, payload, metadata): + LOG.debug("Notification WARN: event_type=%s, payload=%s metadata=%s" + % (event_type, payload, metadata)) + + def error(self, ctxt, publisher_id, event_type, payload, metadata): + LOG.debug("Notification ERROR: event_type=%s, payload=%s metadata=%s" + % (event_type, payload, metadata)) + + class NovaConductorComputeAPI(object): - def __init__(self, topic, scheduler_manager, keystone_manager, msg): - self.topic = topic - self.scheduler_manager = scheduler_manager - self.keystone_manager = keystone_manager - self.target = msg.getTarget(topic=topic + "_synergy", + def __init__(self, synergy_topic, conductor_topic, nova_manager, msg): + self.nova_manager = nova_manager + + self.target = msg.getTarget(topic=synergy_topic, namespace="compute_task", - version="1.10") + version="1.16") self.client = msg.getRPCClient( - target=msg.getTarget(topic=topic, + target=msg.getTarget(topic=conductor_topic, namespace="compute_task", version="1.10")) @@ -62,66 +145,75 @@ class NovaConductorComputeAPI(object): security_groups, block_device_mapping=None, legacy_bdm=True): for instance in instances: + data = {'instances': [instance], + 'image': image, + 'filter_properties': filter_properties, + 'admin_password': admin_password, + 'injected_files': injected_files, + 'requested_networks': requested_networks, + 'security_groups': security_groups, + 'block_device_mapping': block_device_mapping, + 'legacy_bdm': legacy_bdm} + + req = {"context": context, "data": data, + "action": "build_instances"} try: - request = Request.build(context, instance, image, - filter_properties, admin_password, - injected_files, requested_networks, - security_groups, block_device_mapping, - legacy_bdm) + request = Request.fromDict(req) - self.scheduler_manager.processRequest(request) + self.nova_manager.notify(event_type="SERVER_CREATE", + request=request) except Exception as ex: - LOG.error("Exception has occured", exc_info=1) - LOG.error(ex) + LOG.info(ex) - def build_instance(self, context, instance, image, filter_properties, - admin_password, injected_files, requested_networks, - security_groups, block_device_mapping=None, - legacy_bdm=True): - kw = {'instances': [instance], - 'image': image, - 'filter_properties': filter_properties, - 'admin_password': admin_password, - 'injected_files': injected_files, - 'requested_networks': requested_networks, - 'security_groups': security_groups} + def schedule_and_build_instances(self, context, build_requests, + request_specs, image, + admin_password, injected_files, + requested_networks, block_device_mapping): + index = 0 + for build_request in build_requests: + request_spec = request_specs[index] + + index += 1 + + data = {'build_requests': [build_request], + 'request_specs': [request_spec], + 'image': image, + 'admin_password': admin_password, + 'injected_files': injected_files, + 'requested_networks': requested_networks, + 'block_device_mapping': block_device_mapping} + + req = {"context": context, "data": data, + "action": "schedule_and_build_instances"} + + request = Request.fromDict(req) + + self.nova_manager.notify(event_type="SERVER_CREATE", + request=request) + + def build_instance(self, context, action, data): + try: + cctxt = self.client.prepare() + cctxt.cast(context, action, **data) + except Exception as ex: + LOG.info(ex) + + def migrate_server(self, context, **kwargs): cctxt = self.client.prepare() - cctxt.cast(context, 'build_instances', **kw) - - def migrate_server(self, context, instance, scheduler_hint, live, rebuild, - flavor, block_migration, disk_over_commit, - reservations=None, clean_shutdown=True, - request_spec=None): - kw = {'instance': instance, 'scheduler_hint': scheduler_hint, - 'live': live, 'rebuild': rebuild, 'flavor': flavor, - 'block_migration': block_migration, - 'disk_over_commit': disk_over_commit, - 'reservations': reservations, - 'clean_shutdown': clean_shutdown, - 'request_spec': request_spec, - } + return cctxt.call(context, 'migrate_server', **kwargs) + def unshelve_instance(self, context, **kwargs): cctxt = self.client.prepare() - return cctxt.call(context, 'migrate_server', **kw) + cctxt.cast(context, 'unshelve_instance', **kwargs) - def unshelve_instance(self, context, instance): - cctxt = self.client.prepare(version='1.3') - cctxt.cast(context, 'unshelve_instance', instance=instance) + def rebuild_instance(self, ctxt, **kwargs): + cctxt = self.client.prepare() + cctxt.cast(ctxt, 'rebuild_instance', **kwargs) - def rebuild_instance(self, ctxt, instance, new_pass, injected_files, - image_ref, orig_image_ref, orig_sys_metadata, bdms, - recreate=False, on_shared_storage=False, host=None, - preserve_ephemeral=False, kwargs=None): - cctxt = self.client.prepare(version='1.8') - cctxt.cast(ctxt, 'rebuild_instance', - instance=instance, new_pass=new_pass, - injected_files=injected_files, image_ref=image_ref, - orig_image_ref=orig_image_ref, - orig_sys_metadata=orig_sys_metadata, bdms=bdms, - recreate=recreate, on_shared_storage=on_shared_storage, - preserve_ephemeral=preserve_ephemeral, - host=host) + def resize_instance(self, ctxt, **kwargs): + cctxt = self.client.prepare() + cctxt.cast(ctxt, 'resize_instance', **kwargs) class NovaManager(Manager): @@ -170,6 +262,10 @@ class NovaManager(Manager): help="the Synergy topic", default="synergy", required=False), + cfg.StrOpt("notification_topic", + help="the notifiction topic", + default="notifications", + required=False), cfg.StrOpt("conductor_topic", help="the conductor topic", default="conductor", @@ -231,7 +327,6 @@ class NovaManager(Manager): raise Exception("SchedulerManager not found!") self.keystone_manager = self.getManager("KeystoneManager") - self.scheduler_manager = self.getManager("SchedulerManager") amqp_url = self.getParameter("amqp_url") @@ -257,6 +352,8 @@ class NovaManager(Manager): synergy_topic = self.getParameter("synergy_topic") + notification_topic = self.getParameter("notification_topic") + conductor_topic = self.getParameter("conductor_topic") self.getParameter("metadata_proxy_shared_secret", fallback=True) @@ -275,9 +372,9 @@ class NovaManager(Manager): exchange=amqp_exchange) self.novaConductorComputeAPI = NovaConductorComputeAPI( + synergy_topic, conductor_topic, - self.scheduler_manager, - self.keystone_manager, + self, self.messaging) self.conductor_rpc = self.messaging.getRPCServer( @@ -286,13 +383,23 @@ class NovaManager(Manager): endpoints=[self.novaConductorComputeAPI]) self.conductor_rpc.start() + + self.serverEventHandler = ServerEventHandler(self) + + target = self.messaging.getTarget(topic=notification_topic, + exchange=amqp_exchange) + + self.listener = self.messaging.getNotificationListener( + targets=[target], endpoints=[self.serverEventHandler]) + + self.listener.start() except Exception as ex: LOG.error("Exception has occured", exc_info=1) LOG.error("NovaManager initialization failed! %s" % (ex)) raise ex def execute(self, command, *args, **kargs): - raise Exception("command=%r not supported!" % command) + raise Exception("command %r not supported!" % command) def task(self): pass @@ -496,36 +603,11 @@ class NovaManager(Manager): return server - def buildServer(self, request, compute=None): - if compute: - reqId = request.getId() - - self.novaComputeAPI.build_and_run_instance( - request.getContext(), - request.getInstance(), - compute.getHost(), - request.getImage(), - request.getInstance(), - request.getFilterProperties(), - admin_password=request.getAdminPassword(), - injected_files=request.getInjectedFiles(), - requested_networks=request.getRequestedNetworks(), - security_groups=request.getSecurityGroups(), - block_device_mapping=self.getBlockDeviceMappingList(reqId), - node=compute.getNodeName(), - limits=compute.getLimits()) - else: - self.novaConductorComputeAPI.build_instance( - context=request.getContext(), - instance=request.getInstance(), - image=request.getImage(), - filter_properties=request.getFilterProperties(), - admin_password=request.getAdminPassword(), - injected_files=request.getInjectedFiles(), - requested_networks=request.getRequestedNetworks(), - security_groups=request.getSecurityGroups(), - block_device_mapping=request.getBlockDeviceMapping(), - legacy_bdm=request.getLegacyBDM()) + def buildServer(self, request): + self.novaConductorComputeAPI.build_instance( + request.getContext(), + request.getAction(), + request.getData()) def deleteServer(self, server): if not server: @@ -564,33 +646,6 @@ class NovaManager(Manager): return response_data - def setQuotaTypeServer(self, server): - if not server: - return - - QUERY = "insert into nova.instance_metadata (created_at, `key`, " \ - "`value`, instance_uuid) values (%s, 'quota', %s, %s)" - - connection = self.db_engine.connect() - trans = connection.begin() - - quota_type = "private" - - if server.isEphemeral(): - quota_type = "shared" - - try: - connection.execute(QUERY, - [server.getCreatedAt(), quota_type, - server.getId()]) - - trans.commit() - except SQLAlchemyError as ex: - trans.rollback() - raise Exception(ex.message) - finally: - connection.close() - def stopServer(self, server): if not server: return diff --git a/synergy_scheduler_manager/scheduler_manager.py b/synergy_scheduler_manager/scheduler_manager.py index f3170f7..7086306 100644 --- a/synergy_scheduler_manager/scheduler_manager.py +++ b/synergy_scheduler_manager/scheduler_manager.py @@ -1,9 +1,7 @@ import logging -from common.flavor import Flavor from common.quota import SharedQuota from common.request import Request -from common.server import Server from oslo_config import cfg from synergy.common.manager import Manager from synergy.exception import SynergyError @@ -32,125 +30,6 @@ CONF = cfg.CONF LOG = logging.getLogger(__name__) -class Notifications(object): - - def __init__(self, projects, nova_manager): - super(Notifications, self).__init__() - - self.projects = projects - self.nova_manager = nova_manager - - def _makeServer(self, server_info): - if not server_info: - return - - flavor = Flavor() - flavor.setMemory(server_info["memory_mb"]) - flavor.setVCPUs(server_info["vcpus"]) - flavor.setStorage(server_info["root_gb"]) - - if "instance_type" in server_info: - flavor.setName(server_info["instance_type"]) - - server = Server() - server.setFlavor(flavor) - server.setUserId(server_info["user_id"]) - server.setMetadata(server_info["metadata"]) - server.setDeletedAt(server_info["deleted_at"]) - server.setTerminatedAt(server_info["terminated_at"]) - - if "host" in server_info: - server.setHost(server_info["host"]) - - if "uuid" in server_info: - server.setId(server_info["uuid"]) - elif "instance_id" in server_info: - server.setId(server_info["instance_id"]) - - if "project_id" in server_info: - server.setProjectId(server_info["project_id"]) - elif "tenant_id" in server_info: - server.setProjectId(server_info["tenant_id"]) - - if "vm_state" in server_info: - server.setState(server_info["vm_state"]) - elif "state" in server_info: - server.setState(server_info["state"]) - - return server - - def info(self, ctxt, publisher_id, event_type, payload, metadata): - LOG.debug("Notification INFO: event_type=%s payload=%s" - % (event_type, payload)) - - if payload is None or "state" not in payload: - return - - state = payload["state"] - - event_types = ["compute.instance.create.end", - "compute.instance.delete.end", - "compute.instance.update", - "scheduler.run_instance"] - - if event_type not in event_types: - return - - server_info = None - - if event_type == "scheduler.run_instance": - server_info = payload["request_spec"]["instance_type"] - else: - server_info = payload - - server = self._makeServer(server_info) - server_id = server.getId() - host = server.getHost() - - if server.getProjectId() not in self.projects: - return - - if event_type == "compute.instance.create.end" and \ - state == "active": - LOG.info("the server %s is now active on host %s" - % (server_id, host)) - else: - quota = self.projects[server.getProjectId()].getQuota() - - if event_type == "compute.instance.delete.end": - LOG.info("the server %s has been deleted on host %s" - % (server_id, host)) - try: - quota.release(server) - except Exception as ex: - LOG.warn("cannot release server %s " - "(reason=%s)" % (server_id, ex)) - elif state == "error": - LOG.info("error occurred on server %s (host %s)" - % (server_id, host)) - - if not server.getTerminatedAt() and not server.getDeletedAt(): - try: - self.nova_manager.deleteServer(server) - except Exception as ex: - LOG.error("cannot delete server %s: %s" - % (server_id, ex)) - - try: - quota.release(server) - except Exception as ex: - LOG.warn("cannot release server %s " - "(reason=%s)" % (server_id, ex)) - - def warn(self, ctxt, publisher_id, event_type, payload, metadata): - LOG.debug("Notification WARN: event_type=%s, payload=%s metadata=%s" - % (event_type, payload, metadata)) - - def error(self, ctxt, publisher_id, event_type, payload, metadata): - LOG.debug("Notification ERROR: event_type=%s, payload=%s metadata=%s" - % (event_type, payload, metadata)) - - class Worker(Thread): def __init__(self, name, queue, project_manager, nova_manager, @@ -259,8 +138,8 @@ class Worker(Thread): try: self.nova_manager.buildServer(request) - LOG.info("building server %s (user_id=%s prj_id=%s quo" - "ta=shared)" % (server_id, user_id, prj_id)) + LOG.info("building server %s user_id=%s prj_id=%s quo" + "ta=shared" % (server_id, user_id, prj_id)) found = True except SynergyError as ex: @@ -290,7 +169,6 @@ class SchedulerManager(Manager): super(SchedulerManager, self).__init__("SchedulerManager") self.config_opts = [ - cfg.StrOpt("notification_topic", default="notifications"), cfg.IntOpt("backfill_depth", default=100), ] self.workers = [] @@ -321,9 +199,6 @@ class SchedulerManager(Manager): self.fairshare_manager = self.getManager("FairShareManager") self.project_manager = self.getManager("ProjectManager") self.backfill_depth = CONF.SchedulerManager.backfill_depth - self.notification_topic = CONF.SchedulerManager.notification_topic - self.projects = {} - self.listener = None self.exit = False self.configured = False @@ -354,25 +229,62 @@ class SchedulerManager(Manager): self.workers.append(dynamic_worker) - self.notifications = Notifications(self.projects, self.nova_manager) - - target = self.nova_manager.getTarget(topic=self.notification_topic, - exchange="nova") - - self.listener = self.nova_manager.getNotificationListener( - targets=[target], - endpoints=[self.notifications]) - self.quota_manager.deleteExpiredServers() - self.listener.start() self.configured = True def destroy(self): for queue_worker in self.workers: queue_worker.destroy() - def processRequest(self, request): + def doOnEvent(self, event_type, *args, **kwargs): + if event_type == "SERVER_EVENT": + server = kwargs["server"] + event = kwargs["event"] + state = kwargs["state"] + + self._processServerEvent(server, event, state) + elif event_type == "SERVER_CREATE": + self._processServerCreate(kwargs["request"]) + + def _processServerEvent(self, server, event, state): + if event == "compute.instance.create.end" and state == "active": + LOG.info("the server %s is now active on host %s" + % (server.getId(), server.getHost())) + else: + project = self.project_manager.getProject(id=server.getProjectId()) + + if not project: + return + + quota = project.getQuota() + + if event == "compute.instance.delete.end": + LOG.info("the server %s has been deleted on host %s" + % (server.getId(), server.getHost())) + try: + quota.release(server) + except Exception as ex: + LOG.warn("cannot release server %s " + "(reason=%s)" % (server.getId(), ex)) + elif state == "error": + LOG.info("error occurred on server %s (host %s)" + % (server.getId(), server.getHost())) + + if not server.getTerminatedAt() and not server.getDeletedAt(): + try: + self.nova_manager.deleteServer(server) + except Exception as ex: + LOG.error("cannot delete server %s: %s" + % (server.getId(), ex)) + + try: + quota.release(server) + except Exception as ex: + LOG.warn("cannot release server %s " + "(reason=%s)" % (server.getId(), ex)) + + def _processServerCreate(self, request): server = request.getServer() project = self.project_manager.getProject(id=request.getProjectId()) @@ -391,8 +303,8 @@ class SchedulerManager(Manager): if 0 < num_attempts < 3: self.nova_manager.buildServer(request) - LOG.info("retrying to build the server %s (user_id" - "=%s prj_id=%s, num_attempts=%s, reason=%s)" + LOG.info("retrying to build the server %s user_id" + "=%s prj_id=%s, num_attempts=%s, reason=%s" % (request.getId(), request.getUserId(), request.getProjectId(), num_attempts, reason)) return @@ -405,10 +317,10 @@ class SchedulerManager(Manager): request.getProjectId())) self.nova_manager.buildServer(request) - LOG.info("building server %s (user_id=%s prj_id=%s " - "quota=private)" % (server.getId(), - request.getUserId(), - request.getProjectId())) + LOG.info("building server %s user_id=%s prj_id=%s " + "quota=private" % (server.getId(), + request.getUserId(), + request.getProjectId())) else: self.nova_manager.deleteServer(server) LOG.info("request rejected (quota exceeded): " diff --git a/synergy_scheduler_manager/tests/functional/test_scheduler_manager.py b/synergy_scheduler_manager/tests/functional/test_scheduler_manager.py index 499ded6..bfa5b6c 100644 --- a/synergy_scheduler_manager/tests/functional/test_scheduler_manager.py +++ b/synergy_scheduler_manager/tests/functional/test_scheduler_manager.py @@ -13,18 +13,14 @@ from mock import create_autospec from mock import MagicMock from sqlalchemy.engine.base import Engine -from synergy_scheduler_manager.common.flavor import Flavor from synergy_scheduler_manager.common.project import Project from synergy_scheduler_manager.common.queue import QueueDB from synergy_scheduler_manager.common.queue import QueueItem -from synergy_scheduler_manager.common.quota import SharedQuota -from synergy_scheduler_manager.common.server import Server from synergy_scheduler_manager.project_manager import ProjectManager -from synergy_scheduler_manager.scheduler_manager import Notifications from synergy_scheduler_manager.scheduler_manager import Worker from synergy_scheduler_manager.tests.unit import base - +""" class TestNotifications(base.TestCase): def test_info_quota(self): @@ -120,6 +116,7 @@ class TestNotifications(base.TestCase): self.assertEqual(0, SharedQuota.getUsage('vcpus')) self.assertEqual(0, SharedQuota.getUsage('memory')) +""" class TestWorker(base.TestCase): diff --git a/synergy_scheduler_manager/tests/unit/test_request.py b/synergy_scheduler_manager/tests/unit/test_request.py index 42dffb7..9113d70 100644 --- a/synergy_scheduler_manager/tests/unit/test_request.py +++ b/synergy_scheduler_manager/tests/unit/test_request.py @@ -10,6 +10,7 @@ # License for the specific language governing permissions and limitations # under the License. +from datetime import datetime from synergy_scheduler_manager.common.request import Request from synergy_scheduler_manager.tests.unit import base @@ -18,414 +19,278 @@ class TestRequest(base.TestCase): def setUp(self): super(TestRequest, self).setUp() + request_specs = [{ + 'nova_object.version': '1.8', + 'nova_object.namespace': 'nova', + 'nova_object.name': u'RequestSpec', + 'nova_object.data': { + 'requested_destination': None, + 'instance_uuid': '999', + 'retry': { + 'num_attempts': 1, + 'hosts': []}, + 'num_instances': 1, + 'pci_requests': { + 'nova_object.version': '1.1', + 'nova_object.namespace': 'nova', + 'nova_object.name': 'InstancePCIRequests', + 'nova_object.data': {'requests': []}, + 'nova_object.changes': ['requests']}, + 'limits': { + 'nova_object.version': '1.0', + 'nova_object.namespace': 'nova', + 'nova_object.name': 'SchedulerLimits', + 'nova_object.data': { + 'vcpu': None, + 'memory_mb': None, + 'numa_topology': None, + 'disk_gb': None}, + 'nova_object.changes': [ + 'vcpu', 'memory_mb', 'numa_topology', 'disk_gb']}, + 'availability_zone': 'nova', + 'force_nodes': None, + 'image': { + 'nova_object.version': '1.8', + 'nova_object.namespace': 'nova', + 'nova_object.name': 'ImageMeta', + 'nova_object.data': { + 'status': 'active', + 'properties': { + 'nova_object.version': '1.16', + 'nova_object.name': 'ImageMetaProps', + 'nova_object.namespace': 'nova', + 'nova_object.data': {}}, + 'name': 'cirros', + 'container_format': 'bare', + 'created_at': '2017-05-19T12:18:46Z', + 'disk_format': 'qcow2', + 'updated_at': '2017-05-19T12:18:47Z', + 'id': '03d54ef8-f0ac-4ad2-92a0-95835d77d2b5', + 'owner': u'01ab8de5387547d093aa8ae6b85bd8b1', + 'checksum': 'f8ab98ff5e73ebab884d80c9dc9c7290', + 'min_disk': 0, + 'min_ram': 0, + 'size': 13267968}, + 'nova_object.changes': [ + 'status', 'name', 'container_format', 'created_at', + 'disk_format', 'updated_at', 'properties', 'owner', + 'min_ram', 'checksum', 'min_disk', 'id', 'size']}, + 'instance_group': None, + 'force_hosts': None, + 'numa_topology': None, + 'scheduler_hints': {}, + 'flavor': { + 'nova_object.version': '1.1', + 'nova_object.name': 'Flavor', + 'nova_object.namespace': 'nova', + 'nova_object.data': { + 'memory_mb': 512, + 'root_gb': 1, + 'deleted_at': None, + 'name': 'm1.tiny', + 'deleted': False, + 'created_at': '2017-05-23T09:36:21Z', + 'ephemeral_gb': 0, + 'updated_at': None, + 'disabled': False, + 'vcpus': 1, + 'extra_specs': {}, + 'swap': 0, + 'rxtx_factor': 1.0, + 'is_public': True, + 'flavorid': '5cdecdda', + 'vcpu_weight': 0, + 'id': 1}}, + 'project_id': u'01ab8de5387547d093aa8ae6b85bd8b1', + 'id': 126, + 'security_groups': { + 'nova_object.version': '1.0', + 'nova_object.namespace': 'nova', + 'nova_object.name': 'SecurityGroupList', + 'nova_object.data': { + 'objects': [{ + 'nova_object.version': '1.2', + 'nova_object.namespace': 'nova', + 'nova_object.name': 'SecurityGroup', + 'nova_object.data': {'uuid': 'f5b58bc9'}, + 'nova_object.changes': ['uuid']}]}, + 'nova_object.changes': ['objects']}, + 'ignore_hosts': None}, + 'nova_object.changes': ['limits', 'image', 'pci_requests']}] - req_dict = { - 'legacy_bdm': True, - 'requested_networks': None, - 'injected_files': [], - 'block_device_mapping': None, - 'image': { - u'status': u'active', - u'created_at': u'2016-03-23T16:47:10.000000', - u'name': u'cirros', - u'deleted': False, - u'container_format': u'bare', - u'min_ram': 0, - u'disk_format': u'qcow2', - u'updated_at': u'2016-03-23T16:47:10.000000', - u'properties': {}, - u'owner': u'1a6edd87f9ec41d8aa64c8f23d719c2a', - u'checksum': u'ee1eca47dc88f4879d8a229cc70a07c6', - u'min_disk': 0, - u'is_public': True, - u'deleted_at': None, - u'id': u'5100f480-a40c-46cd-b8b6-ea2e5e7bf09e', - u'size': 13287936}, - 'filter_properties': { - u'instance_type': { - u'nova_object.version': u'1.1', - u'nova_object.name': u'Flavor', - u'nova_object.namespace': u'nova', - u'nova_object.data': { - u'memory_mb': 512, - u'root_gb': 1, - u'deleted_at': None, - u'name': u'm1.tiny', - u'deleted': False, - u'created_at': None, - u'ephemeral_gb': 0, - u'updated_at': None, - u'disabled': False, - u'vcpus': 1, - u'extra_specs': {}, - u'swap': 0, - u'rxtx_factor': 1.0, - u'is_public': True, - u'flavorid': u'1', - u'vcpu_weight': 0, - u'id': 2}}, - u'scheduler_hints': {}}, - 'instance': { - u'nova_object.version': u'2.0', - u'nova_object.name': u'Instance', - u'nova_object.namespace': u'nova', - u'nova_object.data': { - u'vm_state': u'building', - u'pci_requests': { - u'nova_object.version': u'1.1', - u'nova_object.name': u'InstancePCIRequests', - u'nova_object.namespace': u'nova', - u'nova_object.data': { - u'instance_uuid': u'e3a7770a-8875e2ccc68b', - u'requests': []}}, - u'availability_zone': None, - u'terminated_at': None, - u'ephemeral_gb': 0, - u'instance_type_id': 2, - u'user_data': None, - u'numa_topology': None, - u'cleaned': False, - u'vm_mode': None, - u'flavor': { - u'nova_object.version': u'1.1', - u'nova_object.name': u'Flavor', - u'nova_object.namespace': u'nova', - u'nova_object.data': { - u'memory_mb': 512, - u'root_gb': 1, - u'deleted_at': None, - u'name': u'm1.tiny', - u'deleted': False, - u'created_at': None, - u'ephemeral_gb': 0, - u'updated_at': None, - u'disabled': False, - u'vcpus': 1, - u'extra_specs': {}, - u'swap': 0, - u'rxtx_factor': 1.0, - u'is_public': True, - u'flavorid': u'1', - u'vcpu_weight': 0, - u'id': 2}}, - u'deleted_at': None, - u'reservation_id': u'r-s9v032d0', - u'id': 830, - u'security_groups': { - u'nova_object.version': u'1.0', - u'nova_object.name': u'SecurityGroupList', - u'nova_object.namespace': u'nova', - u'nova_object.data': { - u'objects': []}}, - u'disable_terminate': False, - u'root_device_name': None, - u'display_name': u'user_b1', - u'uuid': u'e3a7770a-dbf6-4b63-8f9a-8875e2ccc68b', - u'default_swap_device': None, - u'info_cache': { - u'nova_object.version': u'1.5', - u'nova_object.name': u'InstanceInfoCache', - u'nova_object.namespace': u'nova', - u'nova_object.data': { - u'instance_uuid': u'e3a7-8875e2ccc68b', - u'deleted': False, - u'created_at': u'2016-09-02T14:01:39Z', - u'updated_at': None, - u'network_info': u'[]', - u'deleted_at': None}}, - u'hostname': u'user-b1', - u'launched_on': None, - u'display_description': u'user_b1', - u'key_data': None, - u'deleted': False, - u'power_state': 0, - u'key_name': None, - u'default_ephemeral_device': None, - u'progress': 0, - u'project_id': u'd20ac1ffa60841a78a865da63b2399de', - u'launched_at': None, - u'metadata': { - u'quota': u'dynamic', - u'persistent': u'False'}, - u'node': None, - u'ramdisk_id': u'', - u'access_ip_v6': None, - u'access_ip_v4': None, - u'kernel_id': u'', - u'old_flavor': None, - u'updated_at': None, - u'host': None, - u'root_gb': 1, - u'user_id': u'4cb9f71a47914d0c8b78a471fd8f7015', - u'system_metadata': { - u'image_min_disk': u'1', - u'image_min_ram': u'0', - u'image_disk_format': u'qcow2', - u'image_base_image_ref': u'5100f480-a25e7bf09e', - u'image_container_format': u'bare'}, - u'task_state': u'scheduling', - u'shutdown_terminate': False, - u'cell_name': None, - u'ephemeral_key_uuid': None, - u'locked': False, - u'created_at': u'2016-09-02T14:01:39Z', - u'locked_by': None, - u'launch_index': 0, - u'memory_mb': 512, - u'vcpus': 1, - u'image_ref': u'a40c-46cd-b8b6-ea2e5e7bf09e', - u'architecture': None, - u'auto_disk_config': False, - u'os_type': None, - u'config_drive': u'', - u'new_flavor': None}}, - 'admin_password': u'URijD456Cezi', - 'context': { - u'domain': None, - u'project_domain': None, - u'auth_token': u'f9d8458ef4ae454dad75f4636304079c', - u'resource_uuid': None, - u'read_only': False, - u'user_id': u'4cb9f71a47914d0c8b78a471fd8f7015', - u'user_identity': u'fa60841a78a865da63b2399de - - -', - u'tenant': u'd20ac1ffa60841a78a865da63b2399de', - u'instance_lock_checked': False, - u'project_id': u'd20ac1ffa60841a78a865da63b2399de', - u'user_name': u'user_b1', - u'project_name': u'prj_b', - u'timestamp': u'2016-09-02T14:01:39.284558', - u'remote_address': u'10.64.31.19', - u'quota_class': None, - u'is_admin': False, - u'user': u'4cb9f71a47914d0c8b78a471fd8f7015', - u'service_catalog': [], - u'read_deleted': u'no', - u'show_deleted': False, - u'roles': [u'user'], - u'request_id': u'req-69c9e7e6-62b2fee1d6e8', - u'user_domain': None}, - 'security_groups': [u'default']} + build_requests = [{ + 'nova_object.version': '1.2', + 'nova_object.namespace': 'nova', + 'nova_object.name': 'BuildRequest', + 'nova_object.data': { + 'instance_uuid': '999', + 'created_at': '2017-07-20T12:09:27Z', + 'updated_at': None, + 'instance': { + 'nova_object.version': '2.3', + 'nova_object.name': 'Instance', + 'nova_object.namespace': 'nova', + 'nova_object.data': { + 'vm_state': 'building', + 'keypairs': { + 'nova_object.version': '1.3', + 'nova_object.name': 'KeyPairList', + 'nova_object.namespace': 'nova', + 'nova_object.data': {'objects': []}}, + 'pci_requests': { + 'nova_object.version': '1.1', + 'nova_object.name': 'InstancePCIRequests', + 'nova_object.namespace': 'nova', + 'nova_object.data': {'requests': []}}, + 'availability_zone': 'nova', + 'terminated_at': None, + 'ephemeral_gb': 0, + 'old_flavor': None, + 'updated_at': None, + 'numa_topology': None, + 'vm_mode': None, + 'flavor': { + 'nova_object.version': '1.1', + 'nova_object.name': 'Flavor', + 'nova_object.namespace': 'nova', + 'nova_object.data': { + 'memory_mb': 512, + 'root_gb': 1, + 'deleted_at': None, + 'name': 'm1.tiny', + 'deleted': False, + 'created_at': '2017-05-23T09:36:21Z', + 'ephemeral_gb': 0, + 'updated_at': None, + 'disabled': False, + 'vcpus': 1, + 'extra_specs': {}, + 'swap': 0, + 'rxtx_factor': 1.0, + 'is_public': True, + 'flavorid': '5cdecdda', + 'vcpu_weight': 0, + 'id': 1}}, + 'reservation_id': 'r-uavrcsgg', + 'security_groups': { + 'nova_object.version': '1.0', + 'nova_object.name': 'SecurityGroupList', + 'nova_object.namespace': 'nova', + 'nova_object.data': {'objects': []}}, + 'disable_terminate': False, + 'user_id': '4469ff06d1e', + 'uuid': '999', + 'new_flavor': None, + 'info_cache': { + 'nova_object.version': '1.5', + 'nova_object.name': 'InstanceInfoCache', + 'nova_object.namespace': 'nova', + 'nova_object.data': { + 'instance_uuid': '999', + 'network_info': '[]'}}, + 'hostname': 'lisa', + 'launched_on': None, + 'display_description': 'Lisa', + 'key_data': None, + 'deleted': False, + 'power_state': 0, + 'progress': 0, + 'project_id': '1111111', + 'launched_at': None, + 'config_drive': '', + 'node': None, + 'ramdisk_id': '', + 'access_ip_v6': None, + 'access_ip_v4': None, + 'kernel_id': '', + 'key_name': 'mykey', + 'user_data': None, + 'host': None, + 'ephemeral_key_uuid': None, + 'architecture': None, + 'display_name': 'Lisa', + 'system_metadata': { + 'image_min_disk': '1', + 'image_min_ram': '0', + 'image_disk_format': 'qcow2', + 'image_base_image_ref': '03d54ef8', + 'image_container_format': 'bare'}, + 'task_state': 'scheduling', + 'shutdown_terminate': False, + 'tags': { + 'nova_object.version': '1.1', + 'nova_object.name': 'TagList', + 'nova_object.namespace': 'nova', + 'nova_object.data': {u'objects': []}}, + 'cell_name': None, + 'root_gb': 1, + 'locked': False, + 'instance_type_id': 1, + 'locked_by': None, + 'launch_index': 0, + 'memory_mb': 512, + 'vcpus': 1, + 'image_ref': '03d54ef8', + 'root_device_name': None, + 'auto_disk_config': True, + 'os_type': None, + 'metadata': {'quota': 'shared'}, + 'created_at': '2017-07-20T12:09:27Z'}}, + 'project_id': '1111111', + 'id': 63}, + 'nova_object.changes': [u'block_device_mappings']}] - self.req = Request.fromDict(req_dict) + data = {"build_requests": build_requests, + "request_specs": request_specs} - def test_get_AdminPassword(self): - self.assertEqual(u'URijD456Cezi', self.req.getAdminPassword()) + req = {"context": {}, + "data": data, + "action": "schedule_and_build_instances"} - def test_get_Id(self): - self.assertEqual( - u'e3a7770a-dbf6-4b63-8f9a-8875e2ccc68b', - self.req.getId()) + self.req = Request.fromDict(req) - def test_get_Instance(self): - ist = { - u'nova_object.data': { - u'access_ip_v4': None, - u'access_ip_v6': None, - u'architecture': None, - u'auto_disk_config': False, - u'availability_zone': None, - u'cell_name': None, - u'cleaned': False, - u'config_drive': u'', - u'created_at': u'2016-09-02T14:01:39Z', - u'default_ephemeral_device': None, - u'default_swap_device': None, - u'deleted': False, - u'deleted_at': None, - u'disable_terminate': False, - u'display_description': u'user_b1', - u'display_name': u'user_b1', - u'ephemeral_gb': 0, - u'ephemeral_key_uuid': None, - u'flavor': { - u'nova_object.data': { - u'created_at': None, - u'deleted': False, - u'deleted_at': None, - u'disabled': False, - u'ephemeral_gb': 0, - u'extra_specs': {}, - u'flavorid': u'1', - u'id': 2, - u'is_public': True, - u'memory_mb': 512, - u'name': u'm1.tiny', - u'root_gb': 1, - u'rxtx_factor': 1.0, - u'swap': 0, - u'updated_at': None, - u'vcpu_weight': 0, - u'vcpus': 1}, - u'nova_object.name': u'Flavor', - u'nova_object.namespace': u'nova', - u'nova_object.version': u'1.1'}, - u'host': None, - u'hostname': u'user-b1', - u'id': 830, - u'image_ref': u'a40c-46cd-b8b6-ea2e5e7bf09e', - u'info_cache': { - u'nova_object.data': { - u'created_at': u'2016-09-02T14:01:39Z', - u'deleted': False, - u'deleted_at': None, - u'instance_uuid': u'e3a7-8875e2ccc68b', - u'network_info': u'[]', - u'updated_at': None}, - u'nova_object.name': u'InstanceInfoCache', - u'nova_object.namespace': u'nova', - u'nova_object.version': u'1.5'}, - u'instance_type_id': 2, - u'kernel_id': u'', - u'key_data': None, - u'key_name': None, - u'launch_index': 0, - u'launched_at': None, - u'launched_on': None, - u'locked': False, - u'locked_by': None, - u'memory_mb': 512, - u'metadata': { - u'persistent': u'False', - u'quota': u'dynamic'}, - u'new_flavor': None, - u'node': None, - u'numa_topology': None, - u'old_flavor': None, - u'os_type': None, - u'pci_requests': { - u'nova_object.data': { - u'instance_uuid': u'e3a7770a-8875e2ccc68b', - u'requests': []}, - u'nova_object.name': u'InstancePCIRequests', - u'nova_object.namespace': u'nova', - u'nova_object.version': u'1.1'}, - u'power_state': 0, - u'progress': 0, - u'project_id': u'd20ac1ffa60841a78a865da63b2399de', - u'ramdisk_id': u'', - u'reservation_id': u'r-s9v032d0', - u'root_device_name': None, - u'root_gb': 1, - u'security_groups': { - u'nova_object.data': { - u'objects': []}, - u'nova_object.name': u'SecurityGroupList', - u'nova_object.namespace': u'nova', - u'nova_object.version': u'1.0'}, - u'shutdown_terminate': False, - u'system_metadata': { - u'image_base_image_ref': u'5100f480-a25e7bf09e', - u'image_container_format': u'bare', - u'image_disk_format': u'qcow2', - u'image_min_disk': u'1', - u'image_min_ram': u'0'}, - u'task_state': u'scheduling', - u'terminated_at': None, - u'updated_at': None, - u'user_data': None, - u'user_id': u'4cb9f71a47914d0c8b78a471fd8f7015', - u'uuid': u'e3a7770a-dbf6-4b63-8f9a-8875e2ccc68b', - u'vcpus': 1, - u'vm_mode': None, - u'vm_state': u'building'}, - u'nova_object.name': u'Instance', - u'nova_object.namespace': u'nova', - u'nova_object.version': u'2.0'} + def test_request(self): + self.assertIsNotNone(self.req) - self.assertEqual(ist, self.req.getInstance()) + def test_getId(self): + self.assertEqual('999', self.req.getId()) - def test_get_Image(self): - im = {u'checksum': u'ee1eca47dc88f4879d8a229cc70a07c6', - u'container_format': u'bare', - u'created_at': u'2016-03-23T16:47:10.000000', - u'deleted': False, - u'deleted_at': None, - u'disk_format': u'qcow2', - u'id': u'5100f480-a40c-46cd-b8b6-ea2e5e7bf09e', - u'is_public': True, - u'min_disk': 0, - u'min_ram': 0, - u'name': u'cirros', - u'owner': u'1a6edd87f9ec41d8aa64c8f23d719c2a', - u'properties': {}, - u'size': 13287936, - u'status': u'active', - u'updated_at': u'2016-03-23T16:47:10.000000'} + def test_getUserId(self): + self.assertEqual('4469ff06d1e', self.req.getUserId()) - self.assertEqual(im, self.req.getImage()) + def test_getProjectId(self): + self.assertEqual('1111111', self.req.getProjectId()) - def test_get_Context(self): - cont = {u'auth_token': u'f9d8458ef4ae454dad75f4636304079c', - u'domain': None, - u'instance_lock_checked': False, - u'is_admin': False, - u'project_domain': None, - u'project_id': u'd20ac1ffa60841a78a865da63b2399de', - u'project_name': u'prj_b', - u'quota_class': None, - u'read_deleted': u'no', - u'read_only': False, - u'remote_address': u'10.64.31.19', - u'request_id': u'req-69c9e7e6-62b2fee1d6e8', - u'resource_uuid': None, - u'roles': [u'user'], - u'service_catalog': [], - u'show_deleted': False, - u'tenant': u'd20ac1ffa60841a78a865da63b2399de', - u'timestamp': u'2016-09-02T14:01:39.284558', - u'user': u'4cb9f71a47914d0c8b78a471fd8f7015', - u'user_domain': None, - u'user_id': u'4cb9f71a47914d0c8b78a471fd8f7015', - u'user_identity': u'fa60841a78a865da63b2399de - - -', - u'user_name': u'user_b1'} + def test_getAction(self): + self.assertEqual('schedule_and_build_instances', self.req.getAction()) - self.assertEqual(cont, self.req.getContext()) + def test_getContext(self): + self.assertEqual({}, self.req.getContext()) - def test_get_FilterProperties(self): - filt = {u'instance_type': { - u'nova_object.data': {u'created_at': None, - u'deleted': False, - u'deleted_at': None, - u'disabled': False, - u'ephemeral_gb': 0, - u'extra_specs': {}, - u'flavorid': u'1', - u'id': 2, - u'is_public': True, - u'memory_mb': 512, - u'name': u'm1.tiny', - u'root_gb': 1, - u'rxtx_factor': 1.0, - u'swap': 0, - u'updated_at': None, - u'vcpu_weight': 0, - u'vcpus': 1}, - u'nova_object.name': u'Flavor', - u'nova_object.namespace': u'nova', - u'nova_object.version': u'1.1'}, - u'scheduler_hints': {}} + def test_getRetry(self): + self.assertEqual({'num_attempts': 1, 'hosts': []}, self.req.getRetry()) - self.assertEqual(filt, self.req.getFilterProperties()) + def test_getServer(self): + server = self.req.getServer() + self.assertIsNotNone(server) + self.assertEqual('999', server.getId()) + self.assertEqual(datetime(2017, 7, 20, 12, 9, 27), + server.getCreatedAt()) + self.assertEqual('4469ff06d1e', server.getUserId()) + self.assertEqual('1111111', server.getProjectId()) + self.assertEqual('mykey', server.getKeyName()) + self.assertEqual({'quota': 'shared'}, server.getMetadata()) + self.assertFalse(server.isPermanent()) - def test_get_InjectedFiles(self): - self.assertEqual([], self.req.getInjectedFiles()) + flavor = server.getFlavor() + self.assertEqual('5cdecdda', flavor.getId()) + self.assertEqual('m1.tiny', flavor.getName()) + self.assertEqual(512, flavor.getMemory()) + self.assertEqual(1, flavor.getVCPUs()) + self.assertEqual(1, flavor.getStorage()) - def test_get_RequestedNetworks(self): - self.assertEqual(None, self.req.getRequestedNetworks()) - - def test_get_SecurityGroups(self): - self.assertEqual([u'default'], self.req.getSecurityGroups()) - - def test_get_BlockDeviceMapping(self): - self.assertEqual(None, self.req.getBlockDeviceMapping()) - - def test_get_LegacyBDM(self): - self.assertEqual(True, self.req.getLegacyBDM()) - - def test_get_Server(self): - prjId = self.req.getServer().getProjectId() - self.assertEqual('d20ac1ffa60841a78a865da63b2399de', prjId) - - def test_from_to_Dict(self): + def test_toDict(self): rq_dict = self.req.toDict() - self.assertEqual(True, rq_dict['legacy_bdm']) + self.assertIn("action", rq_dict) + self.assertIn("data", rq_dict) + self.assertIn("context", rq_dict)