Add memoization to expensive emulator calls
Drivers of Redfish emulator now cache the outcome of a select calls choosing either the two policies: * Cache the information for the lifetime of the emulator process if it's unlikely for such data to get out of sync * Cache the more dynamic information just for the duration of a single Redfish REST API call This change improves emulator responsiveness. Change-Id: Ief46a0a7b82900ccf6d4c9ebb417f711a9b00513
This commit is contained in:
parent
b6ccaf6da8
commit
b13ea4e9c7
@ -21,6 +21,25 @@ import six
|
|||||||
class AbstractDriver(object):
|
class AbstractDriver(object):
|
||||||
"""Base class for all virtualization drivers"""
|
"""Base class for all virtualization drivers"""
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def initialize(cls, **kwargs):
|
||||||
|
"""Initialize class attributes
|
||||||
|
|
||||||
|
Since drivers may need to cache thing short-term. The emulator
|
||||||
|
instantiates the driver every time it serves a client query.
|
||||||
|
|
||||||
|
Driver objects can cache whenever it makes sense for the duration
|
||||||
|
of a single session. It is guaranteed that the driver object will
|
||||||
|
never be reused for any other session.
|
||||||
|
|
||||||
|
The `initialize` method is provided to set up the driver in a way
|
||||||
|
that would affect all the subsequent sessions.
|
||||||
|
|
||||||
|
:params **kwargs: driver-specific parameters
|
||||||
|
:returns: initialized driver class
|
||||||
|
"""
|
||||||
|
return cls
|
||||||
|
|
||||||
@abc.abstractproperty
|
@abc.abstractproperty
|
||||||
def driver(self):
|
def driver(self):
|
||||||
"""Return human-friendly driver information
|
"""Return human-friendly driver information
|
||||||
|
@ -13,12 +13,13 @@
|
|||||||
# License for the specific language governing permissions and limitations
|
# License for the specific language governing permissions and limitations
|
||||||
# under the License.
|
# under the License.
|
||||||
|
|
||||||
|
from collections import namedtuple
|
||||||
import logging
|
import logging
|
||||||
import uuid
|
import uuid
|
||||||
import xml.etree.ElementTree as ET
|
import xml.etree.ElementTree as ET
|
||||||
|
|
||||||
from collections import namedtuple
|
|
||||||
from sushy_tools.emulator.drivers.base import AbstractDriver
|
from sushy_tools.emulator.drivers.base import AbstractDriver
|
||||||
|
from sushy_tools.emulator.drivers import memoize
|
||||||
from sushy_tools import error
|
from sushy_tools import error
|
||||||
|
|
||||||
try:
|
try:
|
||||||
@ -102,15 +103,17 @@ class LibvirtDriver(AbstractDriver):
|
|||||||
"NicBoot1": "NetworkBoot",
|
"NicBoot1": "NetworkBoot",
|
||||||
"ProcTurboMode": "Enabled"}
|
"ProcTurboMode": "Enabled"}
|
||||||
|
|
||||||
def __init__(self, config, uri=None):
|
@classmethod
|
||||||
self._config = config
|
def initialize(cls, config, uri=None):
|
||||||
self._uri = uri or self.LIBVIRT_URI
|
cls._config = config
|
||||||
self.BOOT_LOADER_MAP = self._config.get(
|
cls._uri = uri or cls.LIBVIRT_URI
|
||||||
'SUSHY_EMULATOR_BOOT_LOADER_MAP', self.BOOT_LOADER_MAP)
|
cls.BOOT_LOADER_MAP = cls._config.get(
|
||||||
self.KNOWN_BOOT_LOADERS = set(y
|
'SUSHY_EMULATOR_BOOT_LOADER_MAP', cls.BOOT_LOADER_MAP)
|
||||||
for x in self.BOOT_LOADER_MAP.values()
|
cls.KNOWN_BOOT_LOADERS = set(y for x in cls.BOOT_LOADER_MAP.values()
|
||||||
for y in x.values())
|
for y in x.values())
|
||||||
|
return cls
|
||||||
|
|
||||||
|
@memoize.memoize()
|
||||||
def _get_domain(self, identity, readonly=False):
|
def _get_domain(self, identity, readonly=False):
|
||||||
with libvirt_open(self._uri, readonly=readonly) as conn:
|
with libvirt_open(self._uri, readonly=readonly) as conn:
|
||||||
try:
|
try:
|
||||||
@ -517,6 +520,7 @@ class LibvirtDriver(AbstractDriver):
|
|||||||
update_existing_attributes)
|
update_existing_attributes)
|
||||||
|
|
||||||
if result.attributes_written:
|
if result.attributes_written:
|
||||||
|
|
||||||
try:
|
try:
|
||||||
with libvirt_open(self._uri) as conn:
|
with libvirt_open(self._uri) as conn:
|
||||||
conn.defineXML(ET.tostring(result.tree).decode('utf-8'))
|
conn.defineXML(ET.tostring(result.tree).decode('utf-8'))
|
||||||
|
56
sushy_tools/emulator/drivers/memoize.py
Normal file
56
sushy_tools/emulator/drivers/memoize.py
Normal file
@ -0,0 +1,56 @@
|
|||||||
|
# Copyright 2018 Red Hat, Inc.
|
||||||
|
# All Rights Reserved.
|
||||||
|
#
|
||||||
|
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||||
|
# not use this file except in compliance with the License. You may obtain
|
||||||
|
# a copy of the License at
|
||||||
|
#
|
||||||
|
# http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
#
|
||||||
|
# Unless required by applicable law or agreed to in writing, software
|
||||||
|
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||||
|
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||||
|
# License for the specific language governing permissions and limitations
|
||||||
|
# under the License.
|
||||||
|
from functools import wraps
|
||||||
|
|
||||||
|
|
||||||
|
def memoize(permanent_cache=None):
|
||||||
|
"""Cache the return value of the decorated method.
|
||||||
|
|
||||||
|
:param permanent_cache: a `dict` like object to use as a cache.
|
||||||
|
If not given, the `._cache` attribute would be added to
|
||||||
|
the object of the decorated method pointing to a newly
|
||||||
|
created `dict`.
|
||||||
|
:return: decorated function
|
||||||
|
"""
|
||||||
|
|
||||||
|
def decorator(method):
|
||||||
|
|
||||||
|
@wraps(method)
|
||||||
|
def wrapped(self, *args, **kwargs):
|
||||||
|
if permanent_cache is None:
|
||||||
|
try:
|
||||||
|
cache = self._cache
|
||||||
|
|
||||||
|
except AttributeError:
|
||||||
|
cache = self._cache = {}
|
||||||
|
|
||||||
|
else:
|
||||||
|
cache = permanent_cache
|
||||||
|
|
||||||
|
method_cache = cache.setdefault(method, {})
|
||||||
|
|
||||||
|
key = frozenset(args), frozenset(kwargs)
|
||||||
|
|
||||||
|
try:
|
||||||
|
return method_cache[key]
|
||||||
|
|
||||||
|
except KeyError:
|
||||||
|
rv = method(self, *args, **kwargs)
|
||||||
|
method_cache[key] = rv
|
||||||
|
return rv
|
||||||
|
|
||||||
|
return wrapped
|
||||||
|
|
||||||
|
return decorator
|
@ -17,6 +17,7 @@ import logging
|
|||||||
import math
|
import math
|
||||||
|
|
||||||
from sushy_tools.emulator.drivers.base import AbstractDriver
|
from sushy_tools.emulator.drivers.base import AbstractDriver
|
||||||
|
from sushy_tools.emulator.drivers import memoize
|
||||||
from sushy_tools import error
|
from sushy_tools import error
|
||||||
|
|
||||||
try:
|
try:
|
||||||
@ -51,11 +52,16 @@ class OpenStackDriver(AbstractDriver):
|
|||||||
|
|
||||||
BOOT_MODE_MAP_REV = {v: k for k, v in BOOT_MODE_MAP.items()}
|
BOOT_MODE_MAP_REV = {v: k for k, v in BOOT_MODE_MAP.items()}
|
||||||
|
|
||||||
def __init__(self, config, os_cloud, readonly=False):
|
PERMANENT_CACHE = {}
|
||||||
self._cc = openstack.connect(cloud=os_cloud)
|
|
||||||
self._os_cloud = os_cloud
|
|
||||||
self._config = config
|
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def initialize(cls, config, os_cloud, readonly=False):
|
||||||
|
cls._cc = openstack.connect(cloud=os_cloud)
|
||||||
|
cls._os_cloud = os_cloud
|
||||||
|
cls._config = config
|
||||||
|
return cls
|
||||||
|
|
||||||
|
@memoize.memoize()
|
||||||
def _get_instance(self, identity):
|
def _get_instance(self, identity):
|
||||||
instance = self._cc.get_server(identity)
|
instance = self._cc.get_server(identity)
|
||||||
if instance:
|
if instance:
|
||||||
@ -72,10 +78,20 @@ class OpenStackDriver(AbstractDriver):
|
|||||||
|
|
||||||
raise error.FishyError(msg)
|
raise error.FishyError(msg)
|
||||||
|
|
||||||
|
@memoize.memoize(permanent_cache=PERMANENT_CACHE)
|
||||||
def _get_flavor(self, identity):
|
def _get_flavor(self, identity):
|
||||||
instance = self._get_instance(identity)
|
instance = self._get_instance(identity)
|
||||||
flavor = self._cc.get_flavor(instance.flavor.id)
|
return self._cc.get_flavor(instance.flavor.id)
|
||||||
return flavor
|
|
||||||
|
@memoize.memoize(permanent_cache=PERMANENT_CACHE)
|
||||||
|
def _get_image_info(self, identity):
|
||||||
|
return self._cc.image.find_image(identity)
|
||||||
|
|
||||||
|
def _get_server_metadata(self, identity):
|
||||||
|
return self._cc.compute.get_server_metadata(identity).to_dict()
|
||||||
|
|
||||||
|
def _set_server_metadata(self, identity, metadata):
|
||||||
|
self._cc.compute.set_server_metadata(identity, metadata)
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def driver(self):
|
def driver(self):
|
||||||
@ -190,7 +206,7 @@ class OpenStackDriver(AbstractDriver):
|
|||||||
except error.FishyError:
|
except error.FishyError:
|
||||||
return
|
return
|
||||||
|
|
||||||
metadata = self._cc.compute.get_server_metadata(instance.id).to_dict()
|
metadata = self._get_server_metadata(instance.id)
|
||||||
|
|
||||||
# NOTE(etingof): the following probably only works with
|
# NOTE(etingof): the following probably only works with
|
||||||
# libvirt-backed compute nodes
|
# libvirt-backed compute nodes
|
||||||
@ -224,7 +240,6 @@ class OpenStackDriver(AbstractDriver):
|
|||||||
|
|
||||||
# NOTE(etingof): the following probably only works with
|
# NOTE(etingof): the following probably only works with
|
||||||
# libvirt-backed compute nodes
|
# libvirt-backed compute nodes
|
||||||
|
|
||||||
self._cc.compute.set_server_metadata(
|
self._cc.compute.set_server_metadata(
|
||||||
instance.id, **{'libvirt:pxe-first': '1'
|
instance.id, **{'libvirt:pxe-first': '1'
|
||||||
if target == 'network' else ''}
|
if target == 'network' else ''}
|
||||||
@ -238,7 +253,7 @@ class OpenStackDriver(AbstractDriver):
|
|||||||
"""
|
"""
|
||||||
instance = self._get_instance(identity)
|
instance = self._get_instance(identity)
|
||||||
|
|
||||||
image = self._cc.image.find_image(instance.image['id'])
|
image = self._get_image_info(instance.image['id'])
|
||||||
|
|
||||||
hw_firmware_type = getattr(image, 'hw_firmware_type', None)
|
hw_firmware_type = getattr(image, 'hw_firmware_type', None)
|
||||||
|
|
||||||
|
@ -14,6 +14,7 @@
|
|||||||
# under the License.
|
# under the License.
|
||||||
|
|
||||||
import argparse
|
import argparse
|
||||||
|
import contextlib
|
||||||
import functools
|
import functools
|
||||||
import json
|
import json
|
||||||
import os
|
import os
|
||||||
@ -32,22 +33,22 @@ app = flask.Flask(__name__)
|
|||||||
# Turn off strict_slashes on all routes
|
# Turn off strict_slashes on all routes
|
||||||
app.url_map.strict_slashes = False
|
app.url_map.strict_slashes = False
|
||||||
|
|
||||||
driver = None
|
DRIVER = None
|
||||||
|
|
||||||
|
|
||||||
def init_virt_driver(decorated_func):
|
def init_virt_driver(decorated_func):
|
||||||
@functools.wraps(decorated_func)
|
@functools.wraps(decorated_func)
|
||||||
def decorator(*args, **kwargs):
|
def decorator(*args, **kwargs):
|
||||||
global driver
|
global DRIVER
|
||||||
|
|
||||||
if driver is None:
|
if DRIVER is None:
|
||||||
|
|
||||||
if 'OS_CLOUD' in os.environ:
|
if 'OS_CLOUD' in os.environ:
|
||||||
if not novadriver.is_loaded:
|
if not novadriver.is_loaded:
|
||||||
app.logger.error('Nova driver not loaded')
|
app.logger.error('Nova driver not loaded')
|
||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
|
|
||||||
driver = novadriver.OpenStackDriver(
|
DRIVER = novadriver.OpenStackDriver.initialize(
|
||||||
app.config, os.environ['OS_CLOUD'])
|
app.config, os.environ['OS_CLOUD'])
|
||||||
|
|
||||||
else:
|
else:
|
||||||
@ -55,7 +56,7 @@ def init_virt_driver(decorated_func):
|
|||||||
app.logger.error('libvirt driver not loaded')
|
app.logger.error('libvirt driver not loaded')
|
||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
|
|
||||||
driver = libvirtdriver.LibvirtDriver(
|
DRIVER = libvirtdriver.LibvirtDriver.initialize(
|
||||||
app.config,
|
app.config,
|
||||||
os.environ.get(
|
os.environ.get(
|
||||||
'SUSHY_EMULATOR_LIBVIRT_URI',
|
'SUSHY_EMULATOR_LIBVIRT_URI',
|
||||||
@ -63,7 +64,7 @@ def init_virt_driver(decorated_func):
|
|||||||
os.environ.get('SUSHY_EMULATOR_LIBVIRT_URL'))
|
os.environ.get('SUSHY_EMULATOR_LIBVIRT_URL'))
|
||||||
)
|
)
|
||||||
|
|
||||||
app.logger.debug('Running with %s', driver.driver)
|
app.logger.debug('Running with %s', DRIVER.driver)
|
||||||
|
|
||||||
return decorated_func(*args, **kwargs)
|
return decorated_func(*args, **kwargs)
|
||||||
|
|
||||||
@ -99,6 +100,11 @@ def ensure_instance_access(decorated_func):
|
|||||||
return decorator
|
return decorator
|
||||||
|
|
||||||
|
|
||||||
|
@contextlib.contextmanager
|
||||||
|
def virt_driver():
|
||||||
|
yield DRIVER()
|
||||||
|
|
||||||
|
|
||||||
def returns_json(decorated_func):
|
def returns_json(decorated_func):
|
||||||
@functools.wraps(decorated_func)
|
@functools.wraps(decorated_func)
|
||||||
def decorator(*args, **kwargs):
|
def decorator(*args, **kwargs):
|
||||||
@ -135,8 +141,9 @@ def root_resource():
|
|||||||
@init_virt_driver
|
@init_virt_driver
|
||||||
@returns_json
|
@returns_json
|
||||||
def system_collection_resource():
|
def system_collection_resource():
|
||||||
systems = [system for system in driver.systems
|
with virt_driver() as driver:
|
||||||
if not instance_denied(identity=system)]
|
systems = [system for system in driver.systems
|
||||||
|
if not instance_denied(identity=system)]
|
||||||
|
|
||||||
app.logger.debug('Serving systems list')
|
app.logger.debug('Serving systems list')
|
||||||
|
|
||||||
@ -153,46 +160,47 @@ def system_resource(identity):
|
|||||||
|
|
||||||
app.logger.debug('Serving resources for system "%s"', identity)
|
app.logger.debug('Serving resources for system "%s"', identity)
|
||||||
|
|
||||||
return flask.render_template(
|
with virt_driver() as driver:
|
||||||
'system.json',
|
return flask.render_template(
|
||||||
identity=identity,
|
'system.json', identity=identity,
|
||||||
name=driver.name(identity),
|
uuid=driver.uuid(identity),
|
||||||
uuid=driver.uuid(identity),
|
power_state=driver.get_power_state(identity),
|
||||||
power_state=driver.get_power_state(identity),
|
total_memory_gb=driver.get_total_memory(identity),
|
||||||
total_memory_gb=driver.get_total_memory(identity),
|
total_cpus=driver.get_total_cpus(identity),
|
||||||
total_cpus=driver.get_total_cpus(identity),
|
boot_source_target=driver.get_boot_device(identity),
|
||||||
boot_source_target=driver.get_boot_device(identity),
|
boot_source_mode=driver.get_boot_mode(identity)
|
||||||
boot_source_mode=driver.get_boot_mode(identity)
|
)
|
||||||
)
|
|
||||||
|
|
||||||
elif flask.request.method == 'PATCH':
|
elif flask.request.method == 'PATCH':
|
||||||
boot = flask.request.json.get('Boot')
|
boot = flask.request.json.get('Boot')
|
||||||
if not boot:
|
if not boot:
|
||||||
return 'PATCH only works for the Boot element', 400
|
return 'PATCH only works for the Boot element', 400
|
||||||
|
|
||||||
target = boot.get('BootSourceOverrideTarget')
|
with virt_driver() as driver:
|
||||||
|
|
||||||
if target:
|
target = boot.get('BootSourceOverrideTarget')
|
||||||
# NOTE(lucasagomes): In libvirt we always set the boot
|
|
||||||
# device frequency to "continuous" so, we are ignoring the
|
|
||||||
# BootSourceOverrideEnabled element here
|
|
||||||
|
|
||||||
driver.set_boot_device(identity, target)
|
if target:
|
||||||
|
# NOTE(lucasagomes): In libvirt we always set the boot
|
||||||
|
# device frequency to "continuous" so, we are ignoring the
|
||||||
|
# BootSourceOverrideEnabled element here
|
||||||
|
|
||||||
app.logger.info('Set boot device to "%s" for system "%s"',
|
driver.set_boot_device(identity, target)
|
||||||
target, identity)
|
|
||||||
|
|
||||||
mode = boot.get('BootSourceOverrideMode')
|
app.logger.info('Set boot device to "%s" for system "%s"',
|
||||||
|
target, identity)
|
||||||
|
|
||||||
if mode:
|
mode = boot.get('BootSourceOverrideMode')
|
||||||
driver.set_boot_mode(identity, mode)
|
|
||||||
|
|
||||||
app.logger.info('Set boot mode to "%s" for system "%s"',
|
if mode:
|
||||||
mode, identity)
|
driver.set_boot_mode(identity, mode)
|
||||||
|
|
||||||
if not target and not mode:
|
app.logger.info('Set boot mode to "%s" for system "%s"',
|
||||||
return ('Missing the BootSourceOverrideTarget and/or '
|
mode, identity)
|
||||||
'BootSourceOverrideMode element', 400)
|
|
||||||
|
if not target and not mode:
|
||||||
|
return ('Missing the BootSourceOverrideTarget and/or '
|
||||||
|
'BootSourceOverrideMode element', 400)
|
||||||
|
|
||||||
return '', 204
|
return '', 204
|
||||||
|
|
||||||
@ -203,7 +211,9 @@ def system_resource(identity):
|
|||||||
@ensure_instance_access
|
@ensure_instance_access
|
||||||
@returns_json
|
@returns_json
|
||||||
def ethernet_interfaces_collection(identity):
|
def ethernet_interfaces_collection(identity):
|
||||||
nics = driver.get_nics(identity)
|
with virt_driver() as driver:
|
||||||
|
nics = driver.get_nics(identity)
|
||||||
|
|
||||||
return flask.render_template(
|
return flask.render_template(
|
||||||
'ethernet_interfaces_collection.json', identity=identity,
|
'ethernet_interfaces_collection.json', identity=identity,
|
||||||
nics=nics)
|
nics=nics)
|
||||||
@ -215,7 +225,9 @@ def ethernet_interfaces_collection(identity):
|
|||||||
@ensure_instance_access
|
@ensure_instance_access
|
||||||
@returns_json
|
@returns_json
|
||||||
def ethernet_interface(identity, nic_id):
|
def ethernet_interface(identity, nic_id):
|
||||||
nics = driver.get_nics(identity)
|
with virt_driver() as driver:
|
||||||
|
nics = driver.get_nics(identity)
|
||||||
|
|
||||||
for nic in nics:
|
for nic in nics:
|
||||||
if nic['id'] == nic_id:
|
if nic['id'] == nic_id:
|
||||||
return flask.render_template(
|
return flask.render_template(
|
||||||
@ -232,7 +244,8 @@ def ethernet_interface(identity, nic_id):
|
|||||||
def system_reset_action(identity):
|
def system_reset_action(identity):
|
||||||
reset_type = flask.request.json.get('ResetType')
|
reset_type = flask.request.json.get('ResetType')
|
||||||
|
|
||||||
driver.set_power_state(identity, reset_type)
|
with virt_driver() as driver:
|
||||||
|
driver.set_power_state(identity, reset_type)
|
||||||
|
|
||||||
app.logger.info('System "%s" power state set to "%s"',
|
app.logger.info('System "%s" power state set to "%s"',
|
||||||
identity, reset_type)
|
identity, reset_type)
|
||||||
@ -245,7 +258,8 @@ def system_reset_action(identity):
|
|||||||
@ensure_instance_access
|
@ensure_instance_access
|
||||||
@returns_json
|
@returns_json
|
||||||
def bios(identity):
|
def bios(identity):
|
||||||
bios = driver.get_bios(identity)
|
with virt_driver() as driver:
|
||||||
|
bios = driver.get_bios(identity)
|
||||||
|
|
||||||
app.logger.debug('Serving BIOS for system "%s"', identity)
|
app.logger.debug('Serving BIOS for system "%s"', identity)
|
||||||
|
|
||||||
@ -263,7 +277,8 @@ def bios(identity):
|
|||||||
def bios_settings(identity):
|
def bios_settings(identity):
|
||||||
|
|
||||||
if flask.request.method == 'GET':
|
if flask.request.method == 'GET':
|
||||||
bios = driver.get_bios(identity)
|
with virt_driver() as driver:
|
||||||
|
bios = driver.get_bios(identity)
|
||||||
|
|
||||||
app.logger.debug('Serving BIOS Settings for system "%s"', identity)
|
app.logger.debug('Serving BIOS Settings for system "%s"', identity)
|
||||||
|
|
||||||
@ -275,7 +290,9 @@ def bios_settings(identity):
|
|||||||
elif flask.request.method == 'PATCH':
|
elif flask.request.method == 'PATCH':
|
||||||
attributes = flask.request.json.get('Attributes')
|
attributes = flask.request.json.get('Attributes')
|
||||||
|
|
||||||
driver.set_bios(identity, attributes)
|
with virt_driver() as driver:
|
||||||
|
driver.set_bios(identity, attributes)
|
||||||
|
|
||||||
app.logger.info('System "%s" BIOS attributes "%s" updated',
|
app.logger.info('System "%s" BIOS attributes "%s" updated',
|
||||||
identity, attributes)
|
identity, attributes)
|
||||||
return '', 204
|
return '', 204
|
||||||
@ -288,8 +305,11 @@ def bios_settings(identity):
|
|||||||
@returns_json
|
@returns_json
|
||||||
def system_reset_bios(identity):
|
def system_reset_bios(identity):
|
||||||
|
|
||||||
driver.reset_bios(identity)
|
with virt_driver() as driver:
|
||||||
|
driver.reset_bios(identity)
|
||||||
|
|
||||||
app.logger.info('BIOS for system "%s" reset', identity)
|
app.logger.info('BIOS for system "%s" reset', identity)
|
||||||
|
|
||||||
return '', 204
|
return '', 204
|
||||||
|
|
||||||
|
|
||||||
@ -336,7 +356,7 @@ def parse_args():
|
|||||||
|
|
||||||
|
|
||||||
def main():
|
def main():
|
||||||
global driver
|
global DRIVER
|
||||||
|
|
||||||
args = parse_args()
|
args = parse_args()
|
||||||
|
|
||||||
@ -350,20 +370,21 @@ def main():
|
|||||||
app.logger.error('Nova driver not loaded')
|
app.logger.error('Nova driver not loaded')
|
||||||
return 1
|
return 1
|
||||||
|
|
||||||
driver = novadriver.OpenStackDriver(app.config, os_cloud)
|
DRIVER = novadriver.OpenStackDriver.initialize(
|
||||||
|
app.config, args.os_cloud)
|
||||||
|
|
||||||
else:
|
else:
|
||||||
if not libvirtdriver.is_loaded:
|
if not libvirtdriver.is_loaded:
|
||||||
app.logger.error('libvirt driver not loaded')
|
app.logger.error('libvirt driver not loaded')
|
||||||
return 1
|
return 1
|
||||||
|
|
||||||
driver = libvirtdriver.LibvirtDriver(
|
DRIVER = libvirtdriver.LibvirtDriver.initialize(
|
||||||
app.config,
|
app.config,
|
||||||
args.libvirt_uri or
|
args.libvirt_uri or
|
||||||
app.config.get('SUSHY_EMULATOR_LIBVIRT_URI', '')
|
app.config.get('SUSHY_EMULATOR_LIBVIRT_URI', '')
|
||||||
)
|
)
|
||||||
|
|
||||||
app.logger.debug('Running with %s', driver.driver)
|
app.logger.debug('Running with %s', DRIVER.driver)
|
||||||
|
|
||||||
ssl_context = None
|
ssl_context = None
|
||||||
|
|
||||||
|
@ -25,7 +25,8 @@ class LibvirtDriverTestCase(base.BaseTestCase):
|
|||||||
uuid = 'c7a5fdbd-cdaf-9455-926a-d65c16db1809'
|
uuid = 'c7a5fdbd-cdaf-9455-926a-d65c16db1809'
|
||||||
|
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
self.test_driver = LibvirtDriver({})
|
test_driver_class = LibvirtDriver.initialize({})
|
||||||
|
self.test_driver = test_driver_class()
|
||||||
super(LibvirtDriverTestCase, self).setUp()
|
super(LibvirtDriverTestCase, self).setUp()
|
||||||
|
|
||||||
@mock.patch('libvirt.open', autospec=True)
|
@mock.patch('libvirt.open', autospec=True)
|
||||||
|
@ -16,23 +16,24 @@ from six.moves import mock
|
|||||||
from sushy_tools.emulator import main
|
from sushy_tools.emulator import main
|
||||||
|
|
||||||
|
|
||||||
@mock.patch('sushy_tools.emulator.main.driver', autospec=True)
|
@mock.patch.object(main, 'DRIVER') # This enables libvirt driver
|
||||||
class EmulatorTestCase(base.BaseTestCase):
|
class EmulatorTestCase(base.BaseTestCase):
|
||||||
|
|
||||||
name = 'QEmu-fedora-i686'
|
name = 'QEmu-fedora-i686'
|
||||||
uuid = 'c7a5fdbd-cdaf-9455-926a-d65c16db1809'
|
uuid = 'c7a5fdbd-cdaf-9455-926a-d65c16db1809'
|
||||||
|
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
main.driver = None
|
main.DRIVER = None
|
||||||
self.app = main.app.test_client()
|
self.app = main.app.test_client()
|
||||||
|
|
||||||
super(EmulatorTestCase, self).setUp()
|
super(EmulatorTestCase, self).setUp()
|
||||||
|
|
||||||
def test_error(self, driver_mock):
|
def test_error(self, driver_mock):
|
||||||
driver_mock.get_power_state.side_effect = Exception('Fish is dead')
|
driver_mock.return_value.get_power_state.side_effect = (
|
||||||
|
Exception('Fish is dead'))
|
||||||
response = self.app.get('/redfish/v1/Systems/' + self.uuid)
|
response = self.app.get('/redfish/v1/Systems/' + self.uuid)
|
||||||
|
|
||||||
self.assertEqual('500 INTERNAL SERVER ERROR', response.status)
|
self.assertEqual(500, response.status_code)
|
||||||
|
|
||||||
def test_root_resource(self, driver_mock):
|
def test_root_resource(self, driver_mock):
|
||||||
response = self.app.get('/redfish/v1/')
|
response = self.app.get('/redfish/v1/')
|
||||||
@ -40,7 +41,7 @@ class EmulatorTestCase(base.BaseTestCase):
|
|||||||
self.assertEqual('RedvirtService', response.json['Id'])
|
self.assertEqual('RedvirtService', response.json['Id'])
|
||||||
|
|
||||||
def test_collection_resource(self, driver_mock):
|
def test_collection_resource(self, driver_mock):
|
||||||
type(driver_mock).systems = mock.PropertyMock(
|
type(driver_mock.return_value).systems = mock.PropertyMock(
|
||||||
return_value=['host0', 'host1'])
|
return_value=['host0', 'host1'])
|
||||||
response = self.app.get('/redfish/v1/Systems')
|
response = self.app.get('/redfish/v1/Systems')
|
||||||
self.assertEqual(200, response.status_code)
|
self.assertEqual(200, response.status_code)
|
||||||
@ -50,12 +51,12 @@ class EmulatorTestCase(base.BaseTestCase):
|
|||||||
response.json['Members'][1])
|
response.json['Members'][1])
|
||||||
|
|
||||||
def test_system_resource_get(self, driver_mock):
|
def test_system_resource_get(self, driver_mock):
|
||||||
driver_mock.uuid.return_value = 'zzzz-yyyy-xxxx'
|
driver_mock.return_value.uuid.return_value = 'zzzz-yyyy-xxxx'
|
||||||
driver_mock.get_power_state.return_value = 'On'
|
driver_mock.return_value.get_power_state.return_value = 'On'
|
||||||
driver_mock.get_total_memory.return_value = 1
|
driver_mock.return_value.get_total_memory.return_value = 1
|
||||||
driver_mock.get_total_cpus.return_value = 2
|
driver_mock.return_value.get_total_cpus.return_value = 2
|
||||||
driver_mock.get_boot_device.return_value = 'Cd'
|
driver_mock.return_value.get_boot_device.return_value = 'Cd'
|
||||||
driver_mock.get_boot_mode.return_value = 'Legacy'
|
driver_mock.return_value.get_boot_mode.return_value = 'Legacy'
|
||||||
|
|
||||||
response = self.app.get('/redfish/v1/Systems/xxxx-yyyy-zzzz')
|
response = self.app.get('/redfish/v1/Systems/xxxx-yyyy-zzzz')
|
||||||
|
|
||||||
@ -76,7 +77,7 @@ class EmulatorTestCase(base.BaseTestCase):
|
|||||||
response = self.app.patch('/redfish/v1/Systems/xxxx-yyyy-zzzz',
|
response = self.app.patch('/redfish/v1/Systems/xxxx-yyyy-zzzz',
|
||||||
json=data)
|
json=data)
|
||||||
self.assertEqual(204, response.status_code)
|
self.assertEqual(204, response.status_code)
|
||||||
set_boot_device = driver_mock.set_boot_device
|
set_boot_device = driver_mock.return_value.set_boot_device
|
||||||
set_boot_device.assert_called_once_with('xxxx-yyyy-zzzz', 'Cd')
|
set_boot_device.assert_called_once_with('xxxx-yyyy-zzzz', 'Cd')
|
||||||
|
|
||||||
def test_system_reset_action_on(self, driver_mock):
|
def test_system_reset_action_on(self, driver_mock):
|
||||||
@ -85,7 +86,7 @@ class EmulatorTestCase(base.BaseTestCase):
|
|||||||
'/redfish/v1/Systems/xxxx-yyyy-zzzz/Actions/ComputerSystem.Reset',
|
'/redfish/v1/Systems/xxxx-yyyy-zzzz/Actions/ComputerSystem.Reset',
|
||||||
json=data)
|
json=data)
|
||||||
self.assertEqual(204, response.status_code)
|
self.assertEqual(204, response.status_code)
|
||||||
set_power_state = driver_mock.set_power_state
|
set_power_state = driver_mock.return_value.set_power_state
|
||||||
set_power_state.assert_called_once_with('xxxx-yyyy-zzzz', 'On')
|
set_power_state.assert_called_once_with('xxxx-yyyy-zzzz', 'On')
|
||||||
|
|
||||||
def test_system_reset_action_forceon(self, driver_mock):
|
def test_system_reset_action_forceon(self, driver_mock):
|
||||||
@ -94,7 +95,7 @@ class EmulatorTestCase(base.BaseTestCase):
|
|||||||
'/redfish/v1/Systems/xxxx-yyyy-zzzz/Actions/ComputerSystem.Reset',
|
'/redfish/v1/Systems/xxxx-yyyy-zzzz/Actions/ComputerSystem.Reset',
|
||||||
json=data)
|
json=data)
|
||||||
self.assertEqual(204, response.status_code)
|
self.assertEqual(204, response.status_code)
|
||||||
set_power_state = driver_mock.set_power_state
|
set_power_state = driver_mock.return_value.set_power_state
|
||||||
set_power_state.assert_called_once_with('xxxx-yyyy-zzzz', 'ForceOn')
|
set_power_state.assert_called_once_with('xxxx-yyyy-zzzz', 'ForceOn')
|
||||||
|
|
||||||
def test_system_reset_action_forceoff(self, driver_mock):
|
def test_system_reset_action_forceoff(self, driver_mock):
|
||||||
@ -103,7 +104,7 @@ class EmulatorTestCase(base.BaseTestCase):
|
|||||||
'/redfish/v1/Systems/xxxx-yyyy-zzzz/Actions/ComputerSystem.Reset',
|
'/redfish/v1/Systems/xxxx-yyyy-zzzz/Actions/ComputerSystem.Reset',
|
||||||
json=data)
|
json=data)
|
||||||
self.assertEqual(204, response.status_code)
|
self.assertEqual(204, response.status_code)
|
||||||
set_power_state = driver_mock.set_power_state
|
set_power_state = driver_mock.return_value.set_power_state
|
||||||
set_power_state.assert_called_once_with('xxxx-yyyy-zzzz', 'ForceOff')
|
set_power_state.assert_called_once_with('xxxx-yyyy-zzzz', 'ForceOff')
|
||||||
|
|
||||||
def test_system_reset_action_shutdown(self, driver_mock):
|
def test_system_reset_action_shutdown(self, driver_mock):
|
||||||
@ -112,7 +113,7 @@ class EmulatorTestCase(base.BaseTestCase):
|
|||||||
'/redfish/v1/Systems/xxxx-yyyy-zzzz/Actions/ComputerSystem.Reset',
|
'/redfish/v1/Systems/xxxx-yyyy-zzzz/Actions/ComputerSystem.Reset',
|
||||||
json=data)
|
json=data)
|
||||||
self.assertEqual(204, response.status_code)
|
self.assertEqual(204, response.status_code)
|
||||||
set_power_state = driver_mock.set_power_state
|
set_power_state = driver_mock.return_value.set_power_state
|
||||||
set_power_state.assert_called_once_with(
|
set_power_state.assert_called_once_with(
|
||||||
'xxxx-yyyy-zzzz', 'GracefulShutdown')
|
'xxxx-yyyy-zzzz', 'GracefulShutdown')
|
||||||
|
|
||||||
@ -122,7 +123,7 @@ class EmulatorTestCase(base.BaseTestCase):
|
|||||||
'/redfish/v1/Systems/xxxx-yyyy-zzzz/Actions/ComputerSystem.Reset',
|
'/redfish/v1/Systems/xxxx-yyyy-zzzz/Actions/ComputerSystem.Reset',
|
||||||
json=data)
|
json=data)
|
||||||
self.assertEqual(204, response.status_code)
|
self.assertEqual(204, response.status_code)
|
||||||
set_power_state = driver_mock.set_power_state
|
set_power_state = driver_mock.return_value.set_power_state
|
||||||
set_power_state.assert_called_once_with(
|
set_power_state.assert_called_once_with(
|
||||||
'xxxx-yyyy-zzzz', 'GracefulRestart')
|
'xxxx-yyyy-zzzz', 'GracefulRestart')
|
||||||
|
|
||||||
@ -132,7 +133,7 @@ class EmulatorTestCase(base.BaseTestCase):
|
|||||||
'/redfish/v1/Systems/xxxx-yyyy-zzzz/Actions/ComputerSystem.Reset',
|
'/redfish/v1/Systems/xxxx-yyyy-zzzz/Actions/ComputerSystem.Reset',
|
||||||
json=data)
|
json=data)
|
||||||
self.assertEqual(204, response.status_code)
|
self.assertEqual(204, response.status_code)
|
||||||
set_power_state = driver_mock.set_power_state
|
set_power_state = driver_mock.return_value.set_power_state
|
||||||
set_power_state.assert_called_once_with(
|
set_power_state.assert_called_once_with(
|
||||||
'xxxx-yyyy-zzzz', 'ForceRestart')
|
'xxxx-yyyy-zzzz', 'ForceRestart')
|
||||||
|
|
||||||
@ -142,7 +143,7 @@ class EmulatorTestCase(base.BaseTestCase):
|
|||||||
'/redfish/v1/Systems/xxxx-yyyy-zzzz/Actions/ComputerSystem.Reset',
|
'/redfish/v1/Systems/xxxx-yyyy-zzzz/Actions/ComputerSystem.Reset',
|
||||||
json=data)
|
json=data)
|
||||||
self.assertEqual(204, response.status_code)
|
self.assertEqual(204, response.status_code)
|
||||||
set_power_state = driver_mock.set_power_state
|
set_power_state = driver_mock.return_value.set_power_state
|
||||||
set_power_state.assert_called_once_with('xxxx-yyyy-zzzz', 'Nmi')
|
set_power_state.assert_called_once_with('xxxx-yyyy-zzzz', 'Nmi')
|
||||||
|
|
||||||
@mock.patch.dict(main.app.config, {}, clear=True)
|
@mock.patch.dict(main.app.config, {}, clear=True)
|
||||||
@ -170,19 +171,20 @@ class EmulatorTestCase(base.BaseTestCase):
|
|||||||
self.assertTrue(main.instance_denied(identity='b'))
|
self.assertTrue(main.instance_denied(identity='b'))
|
||||||
|
|
||||||
def test_get_bios(self, driver_mock):
|
def test_get_bios(self, driver_mock):
|
||||||
driver_mock.get_bios.return_value = {"attribute 1": "value 1",
|
driver_mock.return_value.get_bios.return_value = {
|
||||||
"attribute 2": "value 2"}
|
"attribute 1": "value 1",
|
||||||
|
"attribute 2": "value 2"}
|
||||||
response = self.app.get('/redfish/v1/Systems/' + self.uuid + '/BIOS')
|
response = self.app.get('/redfish/v1/Systems/' + self.uuid + '/BIOS')
|
||||||
|
|
||||||
self.assertEqual('200 OK', response.status)
|
self.assertEqual(200, response.status_code)
|
||||||
self.assertEqual('BIOS', response.json['Id'])
|
self.assertEqual('BIOS', response.json['Id'])
|
||||||
self.assertEqual({"attribute 1": "value 1",
|
self.assertEqual({"attribute 1": "value 1",
|
||||||
"attribute 2": "value 2"},
|
"attribute 2": "value 2"},
|
||||||
response.json['Attributes'])
|
response.json['Attributes'])
|
||||||
|
|
||||||
def test_get_bios_existing(self, driver_mock):
|
def test_get_bios_existing(self, driver_mock):
|
||||||
driver_mock.get_bios.return_value = {"attribute 1": "value 1",
|
driver_mock.return_value.get_bios.return_value = {
|
||||||
"attribute 2": "value 2"}
|
"attribute 1": "value 1", "attribute 2": "value 2"}
|
||||||
response = self.app.get(
|
response = self.app.get(
|
||||||
'/redfish/v1/Systems/' + self.uuid + '/BIOS/Settings')
|
'/redfish/v1/Systems/' + self.uuid + '/BIOS/Settings')
|
||||||
|
|
||||||
@ -194,39 +196,33 @@ class EmulatorTestCase(base.BaseTestCase):
|
|||||||
|
|
||||||
def test_bios_settings_patch(self, driver_mock):
|
def test_bios_settings_patch(self, driver_mock):
|
||||||
data = {'Attributes': {'key': 'value'}}
|
data = {'Attributes': {'key': 'value'}}
|
||||||
self.app.driver = driver_mock
|
|
||||||
response = self.app.patch(
|
response = self.app.patch(
|
||||||
'/redfish/v1/Systems/xxxx-yyyy-zzzz/BIOS/Settings',
|
'/redfish/v1/Systems/xxxx-yyyy-zzzz/BIOS/Settings',
|
||||||
json=data)
|
json=data)
|
||||||
|
|
||||||
self.assertEqual(204, response.status_code)
|
self.assertEqual(204, response.status_code)
|
||||||
driver_mock.set_bios.assert_called_once_with('xxxx-yyyy-zzzz',
|
driver_mock.return_value.set_bios.assert_called_once_with(
|
||||||
{'key': 'value'})
|
'xxxx-yyyy-zzzz', {'key': 'value'})
|
||||||
|
|
||||||
def test_set_bios(self, driver_mock):
|
def test_set_bios(self, driver_mock):
|
||||||
data = {'Attributes': {'key': 'value'}}
|
data = {'Attributes': {'key': 'value'}}
|
||||||
self.app.driver = driver_mock
|
|
||||||
response = self.app.patch(
|
response = self.app.patch(
|
||||||
'/redfish/v1/Systems/xxxx-yyyy-zzzz/BIOS/Settings',
|
'/redfish/v1/Systems/xxxx-yyyy-zzzz/BIOS/Settings',
|
||||||
json=data)
|
json=data)
|
||||||
|
|
||||||
self.assertEqual(204, response.status_code)
|
self.assertEqual(204, response.status_code)
|
||||||
driver_mock.set_bios.assert_called_once_with(
|
driver_mock.return_value.set_bios.assert_called_once_with(
|
||||||
'xxxx-yyyy-zzzz', data['Attributes'])
|
'xxxx-yyyy-zzzz', data['Attributes'])
|
||||||
|
|
||||||
def test_reset_bios(self, driver_mock):
|
def test_reset_bios(self, driver_mock):
|
||||||
self.app.driver = driver_mock
|
|
||||||
response = self.app.post('/redfish/v1/Systems/' + self.uuid +
|
response = self.app.post('/redfish/v1/Systems/' + self.uuid +
|
||||||
'/BIOS/Actions/Bios.ResetBios')
|
'/BIOS/Actions/Bios.ResetBios')
|
||||||
|
|
||||||
self.assertEqual(204, response.status_code)
|
self.assertEqual(204, response.status_code)
|
||||||
driver_mock.reset_bios.assert_called_once_with(self.uuid)
|
driver_mock.return_value.reset_bios.assert_called_once_with(self.uuid)
|
||||||
|
|
||||||
def test_ethernet_interfaces_collection(self, driver_mock):
|
def test_ethernet_interfaces_collection(self, driver_mock):
|
||||||
driver_mock.get_nics.return_value = [{'id': 'nic1',
|
driver_mock.return_value.get_nics.return_value = [
|
||||||
'mac': '52:54:00:4e:5d:37'},
|
{'id': 'nic1', 'mac': '52:54:00:4e:5d:37'},
|
||||||
{'id': 'nic2',
|
{'id': 'nic2', 'mac': '00:11:22:33:44:55'}]
|
||||||
'mac': '00:11:22:33:44:55'}]
|
|
||||||
response = self.app.get('redfish/v1/Systems/' + self.uuid +
|
response = self.app.get('redfish/v1/Systems/' + self.uuid +
|
||||||
'/EthernetInterfaces')
|
'/EthernetInterfaces')
|
||||||
|
|
||||||
@ -241,7 +237,7 @@ class EmulatorTestCase(base.BaseTestCase):
|
|||||||
[m['@odata.id'] for m in response.json['Members']])
|
[m['@odata.id'] for m in response.json['Members']])
|
||||||
|
|
||||||
def test_ethernet_interfaces_collection_empty(self, driver_mock):
|
def test_ethernet_interfaces_collection_empty(self, driver_mock):
|
||||||
driver_mock.get_nics.return_value = []
|
driver_mock.return_value.get_nics.return_value = []
|
||||||
response = self.app.get('redfish/v1/Systems/' + self.uuid +
|
response = self.app.get('redfish/v1/Systems/' + self.uuid +
|
||||||
'/EthernetInterfaces')
|
'/EthernetInterfaces')
|
||||||
|
|
||||||
@ -252,10 +248,9 @@ class EmulatorTestCase(base.BaseTestCase):
|
|||||||
self.assertEqual([], response.json['Members'])
|
self.assertEqual([], response.json['Members'])
|
||||||
|
|
||||||
def test_ethernet_interface(self, driver_mock):
|
def test_ethernet_interface(self, driver_mock):
|
||||||
driver_mock.get_nics.return_value = [{'id': 'nic1',
|
driver_mock.return_value.get_nics.return_value = [
|
||||||
'mac': '52:54:00:4e:5d:37'},
|
{'id': 'nic1', 'mac': '52:54:00:4e:5d:37'},
|
||||||
{'id': 'nic2',
|
{'id': 'nic2', 'mac': '00:11:22:33:44:55'}]
|
||||||
'mac': '00:11:22:33:44:55'}]
|
|
||||||
response = self.app.get('/redfish/v1/Systems/' + self.uuid +
|
response = self.app.get('/redfish/v1/Systems/' + self.uuid +
|
||||||
'/EthernetInterfaces/nic2')
|
'/EthernetInterfaces/nic2')
|
||||||
|
|
||||||
@ -271,10 +266,9 @@ class EmulatorTestCase(base.BaseTestCase):
|
|||||||
response.json['@odata.id'])
|
response.json['@odata.id'])
|
||||||
|
|
||||||
def test_ethernet_interface_not_found(self, driver_mock):
|
def test_ethernet_interface_not_found(self, driver_mock):
|
||||||
driver_mock.get_nics.return_value = [{'id': 'nic1',
|
driver_mock.return_value.get_nics.return_value = [
|
||||||
'mac': '52:54:00:4e:5d:37'},
|
{'id': 'nic1', 'mac': '52:54:00:4e:5d:37'},
|
||||||
{'id': 'nic2',
|
{'id': 'nic2', 'mac': '00:11:22:33:44:55'}]
|
||||||
'mac': '00:11:22:33:44:55'}]
|
|
||||||
response = self.app.get('/redfish/v1/Systems/' + self.uuid +
|
response = self.app.get('/redfish/v1/Systems/' + self.uuid +
|
||||||
'/EthernetInterfaces/nic3')
|
'/EthernetInterfaces/nic3')
|
||||||
|
|
||||||
|
81
sushy_tools/tests/unit/emulator/test_memoize.py
Normal file
81
sushy_tools/tests/unit/emulator/test_memoize.py
Normal file
@ -0,0 +1,81 @@
|
|||||||
|
# Copyright 2018 Red Hat, Inc.
|
||||||
|
# All Rights Reserved.
|
||||||
|
#
|
||||||
|
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||||
|
# not use this file except in compliance with the License. You may obtain
|
||||||
|
# a copy of the License at
|
||||||
|
#
|
||||||
|
# http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
#
|
||||||
|
# Unless required by applicable law or agreed to in writing, software
|
||||||
|
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||||
|
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||||
|
# License for the specific language governing permissions and limitations
|
||||||
|
# under the License.
|
||||||
|
|
||||||
|
from oslotest import base
|
||||||
|
|
||||||
|
from sushy_tools.emulator.drivers import memoize
|
||||||
|
|
||||||
|
|
||||||
|
class MemoizeTestCase(base.BaseTestCase):
|
||||||
|
|
||||||
|
def test_local_cache(self):
|
||||||
|
|
||||||
|
class Driver(object):
|
||||||
|
call_count = 0
|
||||||
|
|
||||||
|
@memoize.memoize()
|
||||||
|
def fun(self, *args, **kwargs):
|
||||||
|
self.call_count += 1
|
||||||
|
return args, kwargs
|
||||||
|
|
||||||
|
driver = Driver()
|
||||||
|
|
||||||
|
result = driver.fun(1, x=2)
|
||||||
|
self.assertEqual(((1,), {'x': 2}), result)
|
||||||
|
|
||||||
|
result = driver.fun(1, x=2)
|
||||||
|
self.assertEqual(((1,), {'x': 2}), result)
|
||||||
|
|
||||||
|
# Due to memoization, expect just one call
|
||||||
|
self.assertEqual(1, driver.call_count)
|
||||||
|
|
||||||
|
driver = Driver()
|
||||||
|
|
||||||
|
result = driver.fun(1, x=2)
|
||||||
|
self.assertEqual(((1,), {'x': 2}), result)
|
||||||
|
|
||||||
|
# Due to volatile cache, expect one more call
|
||||||
|
self.assertEqual(1, driver.call_count)
|
||||||
|
|
||||||
|
def test_external_cache(self):
|
||||||
|
|
||||||
|
permanent_cache = {}
|
||||||
|
|
||||||
|
class Driver(object):
|
||||||
|
call_count = 0
|
||||||
|
|
||||||
|
@memoize.memoize(permanent_cache=permanent_cache)
|
||||||
|
def fun(self, *args, **kwargs):
|
||||||
|
self.call_count += 1
|
||||||
|
return args, kwargs
|
||||||
|
|
||||||
|
driver = Driver()
|
||||||
|
|
||||||
|
result = driver.fun(1, x=2)
|
||||||
|
self.assertEqual(((1,), {'x': 2}), result)
|
||||||
|
|
||||||
|
result = driver.fun(1, x=2)
|
||||||
|
self.assertEqual(((1,), {'x': 2}), result)
|
||||||
|
|
||||||
|
# Due to memoization, expect just one call
|
||||||
|
self.assertEqual(1, driver.call_count)
|
||||||
|
|
||||||
|
driver = Driver()
|
||||||
|
|
||||||
|
result = driver.fun(1, x=2)
|
||||||
|
self.assertEqual(((1,), {'x': 2}), result)
|
||||||
|
|
||||||
|
# Due to permanent cache, expect no more calls
|
||||||
|
self.assertEqual(0, driver.call_count)
|
@ -21,6 +21,7 @@ from sushy_tools.emulator.drivers.novadriver import OpenStackDriver
|
|||||||
from sushy_tools import error
|
from sushy_tools import error
|
||||||
|
|
||||||
|
|
||||||
|
@mock.patch.dict(OpenStackDriver.PERMANENT_CACHE)
|
||||||
class NovaDriverTestCase(base.BaseTestCase):
|
class NovaDriverTestCase(base.BaseTestCase):
|
||||||
|
|
||||||
name = 'QEmu-fedora-i686'
|
name = 'QEmu-fedora-i686'
|
||||||
@ -30,7 +31,8 @@ class NovaDriverTestCase(base.BaseTestCase):
|
|||||||
self.nova_patcher = mock.patch('openstack.connect', autospec=True)
|
self.nova_patcher = mock.patch('openstack.connect', autospec=True)
|
||||||
self.nova_mock = self.nova_patcher.start()
|
self.nova_mock = self.nova_patcher.start()
|
||||||
|
|
||||||
self.test_driver = OpenStackDriver({}, 'fake-cloud')
|
test_driver_class = OpenStackDriver.initialize({}, 'fake-cloud')
|
||||||
|
self.test_driver = test_driver_class()
|
||||||
|
|
||||||
super(NovaDriverTestCase, self).setUp()
|
super(NovaDriverTestCase, self).setUp()
|
||||||
|
|
||||||
@ -145,6 +147,7 @@ class NovaDriverTestCase(base.BaseTestCase):
|
|||||||
self.nova_mock.return_value.get_server.return_value = server
|
self.nova_mock.return_value.get_server.return_value = server
|
||||||
|
|
||||||
image = mock.Mock(hw_firmware_type='bios')
|
image = mock.Mock(hw_firmware_type='bios')
|
||||||
|
|
||||||
self.nova_mock.return_value.image.find_image.return_value = image
|
self.nova_mock.return_value.image.find_image.return_value = image
|
||||||
|
|
||||||
boot_mode = self.test_driver.get_boot_mode(self.uuid)
|
boot_mode = self.test_driver.get_boot_mode(self.uuid)
|
||||||
@ -216,7 +219,9 @@ class NovaDriverTestCase(base.BaseTestCase):
|
|||||||
|
|
||||||
server = mock.Mock(id=self.uuid, addresses=addresses)
|
server = mock.Mock(id=self.uuid, addresses=addresses)
|
||||||
self.nova_mock.return_value.get_server.return_value = server
|
self.nova_mock.return_value.get_server.return_value = server
|
||||||
|
|
||||||
nics = self.test_driver.get_nics(self.uuid)
|
nics = self.test_driver.get_nics(self.uuid)
|
||||||
|
|
||||||
self.assertEqual([{'id': 'fa:16:3e:22:18:31',
|
self.assertEqual([{'id': 'fa:16:3e:22:18:31',
|
||||||
'mac': 'fa:16:3e:22:18:31'},
|
'mac': 'fa:16:3e:22:18:31'},
|
||||||
{'id': 'fa:16:3e:46:e3:ac',
|
{'id': 'fa:16:3e:46:e3:ac',
|
||||||
|
Loading…
Reference in New Issue
Block a user