Added support for OpenStack Ocata to NovaManager

This commit updates NovaManager for supporting the Ocata version

BUG: #1704801
Change-Id: Icad9c14b14f34d16861caf82845b0e847085556c
Sem-Ver: feature
This commit is contained in:
Lisa Zangrando 2017-07-31 14:59:49 +02:00
parent 94456be8fc
commit 5f28805ca7
6 changed files with 593 additions and 802 deletions

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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): "

View File

@ -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):

View File

@ -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)