Adding Exists Event Publishing

TaskManager periodically sends notifications for active instances

Implements: blueprint notification-exists-event

Change-Id: If9ec0a6b89ee28c550297e5a7450b0b927e034f2
This commit is contained in:
justin-hopper 2013-05-15 16:28:04 -07:00
parent d1c996b7d1
commit 3a276f77c3
11 changed files with 517 additions and 59 deletions

View File

@ -40,9 +40,9 @@ api_extensions_path = reddwarf/extensions
# These options are for an admin user in your keystone config.
# It proxy's the token received from the user to send to nova via this admin users creds,
# basically acting like the client via that proxy token.
reddwarf_proxy_admin_user = admin
reddwarf_proxy_admin_pass = 3de4922d8b6ac5a1aad9
reddwarf_proxy_admin_tenant_name = admin
nova_proxy_admin_user = admin
nova_proxy_admin_pass = 3de4922d8b6ac5a1aad9
nova_proxy_admin_tenant_name = admin
reddwarf_auth_url = http://0.0.0.0:5000/v2.0
swift_url = http://10.0.0.1:8080/v1/AUTH_

View File

@ -44,14 +44,20 @@ server_delete_time_out=480
# These options are for an admin user in your keystone config.
# It proxy's the token received from the user to send to nova via this admin users creds,
# basically acting like the client via that proxy token.
reddwarf_proxy_admin_user = admin
reddwarf_proxy_admin_pass = 3de4922d8b6ac5a1aad9
reddwarf_proxy_admin_tenant_name = admin
nova_proxy_admin_user = admin
nova_proxy_admin_pass = 3de4922d8b6ac5a1aad9
nova_proxy_admin_tenant_name = admin
reddwarf_auth_url = http://0.0.0.0:5000/v2.0
# Manager impl for the taskmanager
taskmanager_manager=reddwarf.taskmanager.manager.Manager
# Manager sends Exists Notifications
taskmanager_exists_notification = True
exists_notification_transformer = reddwarf.extensions.mgmt.instances.models.NovaNotificationTransformer
exists_notification_ticks = 30
notification_service_id = 2f3ff068-2bfb-4f70-9a9d-a6bb65bc084b
# Reddwarf DNS
reddwarf_dns_support = False
@ -63,9 +69,6 @@ agent_call_high_timeout = 150
# Whether to use nova's contrib api for create server with volume
use_nova_server_volume = False
# usage notifications
notification_driver = reddwarf.tests.util.usage
# ============ notifer queue kombu connection options ========================
notifier_queue_hostname = localhost
@ -76,6 +79,10 @@ notifier_queue_port = 5672
notifier_queue_virtual_host = /
notifier_queue_transport = memory
# usage notifications
notification_driver=reddwarf.openstack.common.notifier.rpc_notifier
control_exchange=reddwarf
# ============ Logging information =============================
#log_dir = /integration/report
#log_file = reddwarf-taskmanager.log

View File

@ -105,6 +105,8 @@ notifier_queue_port = 5672
notifier_queue_virtual_host = /
notifier_queue_transport = memory
control_exchange = reddwarf
# ============ Logging information =============================
#log_dir = /integration/report
#log_file = reddwarf-api.log

View File

@ -55,9 +55,9 @@ api_extensions_path = reddwarf/extensions
# These options are for an admin user in your keystone config.
# It proxy's the token received from the user to send to nova via this admin users creds,
# basically acting like the client via that proxy token.
reddwarf_proxy_admin_user = admin
reddwarf_proxy_admin_pass = 3de4922d8b6ac5a1aad9
reddwarf_proxy_admin_tenant_name = admin
nova_proxy_admin_user = admin
nova_proxy_admin_pass = 3de4922d8b6ac5a1aad9
nova_proxy_admin_tenant_name = admin
reddwarf_auth_url = http://0.0.0.0:5000/v2.0
nova_region_name = RegionOne
@ -116,6 +116,8 @@ notifier_queue_port = 5672
notifier_queue_virtual_host = /
notifier_queue_transport = memory
control_exchange = reddwarf
paste_config_file=api-paste.ini.test
[composite:reddwarf]

View File

@ -45,7 +45,8 @@ common_opts = [
cfg.StrOpt('swift_url', default='http://localhost:8080/v1/AUTH_'),
cfg.StrOpt('reddwarf_auth_url', default='http://0.0.0.0:5000/v2.0'),
cfg.StrOpt('host', default='0.0.0.0'),
cfg.IntOpt('report_interval', default=10),
cfg.IntOpt('report_interval', default=10,
help='The interval in seconds which periodic tasks are run'),
cfg.IntOpt('periodic_interval', default=60),
cfg.BoolOpt('reddwarf_dns_support', default=False),
cfg.StrOpt('db_api_implementation', default='reddwarf.db.sqlalchemy.api'),
@ -116,33 +117,33 @@ common_opts = [
cfg.IntOpt('reddwarf_security_group_rule_port', default=3306),
cfg.IntOpt('reddwarf_api_workers', default=None),
cfg.IntOpt('usage_sleep_time', default=1,
help="Time to sleep during the check active guest"),
help='Time to sleep during the check active guest'),
cfg.IntOpt('usage_timeout', default=300,
help="Timeout to wait for an guest to become active"),
help='Timeout to wait for an guest to become active'),
cfg.StrOpt('region', default='LOCAL_DEV',
help="The region this service is located."),
help='The region this service is located.'),
cfg.StrOpt('backup_runner',
default='reddwarf.guestagent.backup.backup_types.InnoBackupEx'),
cfg.StrOpt('backup_strategy', default='InnoBackupEx',
help="Default strategy to perform backups"),
help='Default strategy to perform backups'),
cfg.StrOpt('backup_namespace',
default='reddwarf.guestagent.strategies.backup.impl',
help="Namespace to load backup strategies from"),
help='Namespace to load backup strategies from'),
cfg.StrOpt('restore_namespace',
default='reddwarf.guestagent.strategies.restore.impl',
help="Namespace to load restore strategies from"),
help='Namespace to load restore strategies from'),
cfg.StrOpt('storage_strategy', default='SwiftStorage',
help="Default strategy to store backups"),
cfg.StrOpt('storage_namespace',
default='reddwarf.guestagent.strategies.storage.swift',
help="Namespace to load the default storage strategy from"),
help='Namespace to load the default storage strategy from'),
cfg.StrOpt('backup_swift_container', default='database_backups'),
cfg.BoolOpt('backup_use_gzip_compression', default=True,
help="Compress backups using gzip."),
help='Compress backups using gzip.'),
cfg.BoolOpt('backup_use_snet', default=False,
help="Send backup files over snet."),
help='Send backup files over snet.'),
cfg.IntOpt('backup_chunk_size', default=2 ** 16,
help="Chunk size to stream to swift container."),
help='Chunk size to stream to swift container'),
cfg.IntOpt('backup_segment_max_size', default=2 * (1024 ** 3),
help="Maximum size of each segment of the backup file."),
cfg.StrOpt('remote_dns_client',
@ -155,6 +156,21 @@ common_opts = [
default='reddwarf.common.remote.nova_volume_client'),
cfg.StrOpt('remote_swift_client',
default='reddwarf.common.remote.swift_client'),
cfg.BoolOpt('taskmanager_exists_notification', default=False,
help='Toggles Task Manager to send out exists notifications'),
cfg.StrOpt('exists_notification_transformer', default=None,
help='Transformer for exists notifications'),
cfg.IntOpt('exists_notification_ticks', default=360,
help='Number of report_intevals to wait between pushing events '
'(see report_interval)'),
cfg.StrOpt('notification_service_id', default='',
help='Unique ID to tag notification events'),
cfg.StrOpt('nova_proxy_admin_user', default='',
help="Admin username used to connect to Nova"),
cfg.StrOpt('nova_proxy_admin_pass', default='',
help="Admin password used to connect to Nova"),
cfg.StrOpt('nova_proxy_admin_tenant_name', default='',
help="Admin tenant used to connect to Nova")
]

View File

@ -48,6 +48,16 @@ def nova_client(context):
return client
def create_admin_nova_client(context):
"""
Creates client that uses reddwarf admin credentials
:return: a client for nova for the reddwarf admin
"""
client = create_nova_client(context)
client.client.auth_token = None
return client
def nova_volume_client(context):
# Quite annoying but due to a paste config loading bug.
# TODO(hub-cap): talk to the openstack-common people about this

View File

@ -11,39 +11,44 @@
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
import datetime
from reddwarf.common import cfg
from reddwarf.common import remote
from reddwarf.common import utils
from reddwarf.openstack.common import log as logging
from reddwarf.common.remote import create_nova_client
from reddwarf.common.remote import create_nova_volume_client
from reddwarf.openstack.common.notifier import api as notifier
from reddwarf.instance import models as imodels
from reddwarf.instance.models import load_instance
from reddwarf.instance.models import load_instance, InstanceServiceStatus
from reddwarf.instance import models as instance_models
from reddwarf.extensions.mysql import models as mysql_models
LOG = logging.getLogger(__name__)
CONF = cfg.CONF
def load_mgmt_instances(context, deleted=None):
client = create_nova_client(context)
mgmt_servers = client.rdservers.list()
db_infos = None
def load_mgmt_instances(context, deleted=None, client=None):
if not client:
client = remote.create_nova_client(context)
try:
mgmt_servers = client.rdservers.list()
except AttributeError:
mgmt_servers = client.servers.list(search_opts={'all_tenants': 1})
LOG.info("Found %d servers in Nova" %
len(mgmt_servers if mgmt_servers else []))
if deleted is not None:
db_infos = instance_models.DBInstance.find_all(deleted=deleted)
else:
db_infos = instance_models.DBInstance.find_all()
instances = MgmtInstances.load_status_from_existing(
context,
db_infos,
mgmt_servers)
instances = MgmtInstances.load_status_from_existing(context, db_infos,
mgmt_servers)
return instances
def load_mgmt_instance(cls, context, id):
try:
instance = load_instance(cls, context, id, needs_server=True)
client = create_nova_client(context)
client = remote.create_nova_client(context)
server = client.rdservers.get(instance.server_id)
instance.server.host = server.host
instance.server.deleted = server.deleted
@ -57,7 +62,6 @@ def load_mgmt_instance(cls, context, id):
class SimpleMgmtInstance(imodels.BaseInstance):
def __init__(self, context, db_info, server, service_status):
super(SimpleMgmtInstance, self).__init__(context, db_info, server,
service_status)
@ -86,7 +90,6 @@ class SimpleMgmtInstance(imodels.BaseInstance):
class DetailedMgmtInstance(SimpleMgmtInstance):
def __init__(self, *args, **kwargs):
super(DetailedMgmtInstance, self).__init__(*args, **kwargs)
self.volume = None
@ -96,12 +99,12 @@ class DetailedMgmtInstance(SimpleMgmtInstance):
@classmethod
def load(cls, context, id):
instance = load_mgmt_instance(cls, context, id)
client = create_nova_volume_client(context)
client = remote.create_nova_volume_client(context)
try:
instance.volume = client.volumes.get(instance.volume_id)
except Exception as ex:
except Exception:
instance.volume = None
# Populate the volume_used attribute from the guest agent.
# Populate the volume_used attribute from the guest agent.
instance_models.load_guest_info(instance, context, id)
instance.root_history = mysql_models.RootHistory.load(context=context,
instance_id=id)
@ -109,7 +112,6 @@ class DetailedMgmtInstance(SimpleMgmtInstance):
class MgmtInstance(imodels.Instance):
def get_diagnostics(self):
return self.get_guest().get_diagnostics()
@ -121,10 +123,8 @@ class MgmtInstance(imodels.Instance):
class MgmtInstances(imodels.Instances):
@staticmethod
def load_status_from_existing(context, db_infos, servers):
def load_instance(context, db, status, server=None):
return SimpleMgmtInstance(context, db, server, status)
@ -132,7 +132,8 @@ class MgmtInstances(imodels.Instances):
raise TypeError("Argument context not defined.")
find_server = imodels.create_server_list_matcher(servers)
instances = imodels.Instances._load_servers_status(load_instance,
context, db_infos,
context,
db_infos,
find_server)
_load_servers(instances, find_server)
return instances
@ -148,3 +149,91 @@ def _load_servers(instances, find_server):
except Exception as ex:
LOG.error(ex)
return instances
def publish_exist_events(transformer, admin_context):
notifications = transformer()
for notification in notifications:
notifier.notify(admin_context,
CONF.host,
"reddwarf.instance.exists",
'INFO',
notification)
class NotificationTransformer(object):
def __init__(self, **kwargs):
pass
@staticmethod
def _get_audit_period():
now = datetime.datetime.now()
audit_start = utils.isotime(now, subsecond=True)
audit_end = utils.isotime(
now + datetime.timedelta(
seconds=CONF.exists_notification_ticks * CONF.report_interval),
subsecond=True)
return audit_start, audit_end
def transform_instance(self, instance, audit_start, audit_end):
return {'audit-period-beginning': audit_start,
'audit-period-ending': audit_end,
'created_at': instance.created,
'display_name': instance.name,
'instance_id': instance.id,
'instance_name': instance.name,
'instance_type_id': instance.flavor_id,
'launched_at': instance.created,
'nova_instance_id': instance.server_id,
'region': CONF.region,
'state_description': instance.status.lower(),
'state': instance.status.lower(),
'tenant_id': instance.tenant_id,
'service_id': CONF.notification_service_id}
def __call__(self):
audit_start, audit_end = NotificationTransformer._get_audit_period()
messages = []
db_infos = instance_models.DBInstance.find_all(deleted=False)
for db_info in db_infos:
service_status = InstanceServiceStatus.find_by(
instance_id=db_info.id)
instance = SimpleMgmtInstance(None, db_info, None, service_status)
message = self.transform_instance(instance, audit_start, audit_end)
messages.append(message)
return messages
class NovaNotificationTransformer(NotificationTransformer):
def __init__(self, **kwargs):
super(NovaNotificationTransformer, self).__init__(**kwargs)
self.context = kwargs['context']
self.nova_client = remote.create_admin_nova_client(self.context)
self._flavor_cache = {}
def _lookup_flavor(self, flavor_id):
if flavor_id in self._flavor_cache:
LOG.debug("Flavor cache hit for %s" % flavor_id)
return self._flavor_cache[flavor_id]
# fetch flavor resource from nova
LOG.info("Flavor cache miss for %s" % flavor_id)
flavor = self.nova_client.flavors.get(flavor_id)
self._flavor_cache[flavor_id] = flavor.name if flavor else 'unknown'
return self._flavor_cache[flavor_id]
def __call__(self):
audit_start, audit_end = NotificationTransformer._get_audit_period()
instances = load_mgmt_instances(self.context, deleted=False,
client=self.nova_client)
messages = []
for instance in filter(
lambda inst: inst.status != 'SHUTDOWN' and inst.server,
instances):
message = {
'instance_type': self._lookup_flavor(instance.flavor_id),
'user_id': instance.server.user_id}
message.update(self.transform_instance(instance,
audit_start,
audit_end))
messages.append(message)
return messages

View File

@ -45,7 +45,7 @@ def load_server(context, instance_id, server_id):
client = create_nova_client(context)
try:
server = client.servers.get(server_id)
except nova_exceptions.NotFound as e:
except nova_exceptions.NotFound:
LOG.debug("Could not find nova server_id(%s)" % server_id)
raise exception.ComputeInstanceNotFound(instance_id=instance_id,
server_id=server_id)
@ -89,7 +89,7 @@ def load_simple_instance_server_status(context, db_info):
server = client.servers.get(db_info.compute_instance_id)
db_info.server_status = server.status
db_info.addresses = server.addresses
except nova_exceptions.NotFound, e:
except nova_exceptions.NotFound:
db_info.server_status = "SHUTDOWN"
db_info.addresses = {}
@ -250,8 +250,6 @@ def get_db_info(context, id):
db_info = DBInstance.find_by(id=id, deleted=False)
except exception.NotFound:
raise exception.NotFound(uuid=id)
except exception.ModelNotFoundError:
raise exception.NotFound(uuid=id)
if not context.is_admin and db_info.tenant_id != context.tenant:
LOG.error("Tenant %s tried to access instance %s, owned by %s."
% (context.tenant, id, db_info.tenant_id))
@ -264,7 +262,7 @@ def load_any_instance(context, id):
# If that fails, try to load it without the server.
try:
return load_instance(BuiltInstance, context, id, needs_server=True)
except exception.UnprocessableEntity as upe:
except exception.UnprocessableEntity:
LOG.warn("Could not load instance %s." % id)
return load_instance(FreshInstance, context, id, needs_server=False)
@ -572,7 +570,6 @@ class Instance(BuiltInstance):
"""
Raises exception if an instance action cannot currently be performed.
"""
status = None
if self.db_info.server_status != 'ACTIVE':
status = self.db_info.server_status
elif self.db_info.task_status != InstanceTasks.NONE:
@ -616,7 +613,7 @@ class Instances(object):
@staticmethod
def load(context):
def load_simple_instance(context, db, status):
def load_simple_instance(context, db, status, **kwargs):
return SimpleInstance(context, db, status)
if context is None:
@ -662,16 +659,16 @@ class Instances(object):
#volumes = find_volumes(server.id)
status = InstanceServiceStatus.find_by(instance_id=db.id)
LOG.info(_("Server api_status(%s)") %
(status.status.api_status))
status.status.api_status)
if not status.status: # This should never happen.
LOG.error(_("Server status could not be read for "
"instance id(%s)") % (db.id))
"instance id(%s)") % db.id)
continue
except exception.ModelNotFoundError:
LOG.error(_("Server status could not be read for "
"instance id(%s)") % (db.id))
"instance id(%s)") % db.id)
continue
ret.append(load_instance(context, db, status))
ret.append(load_instance(context, db, status, server=server))
return ret
@ -684,7 +681,7 @@ class DBInstance(dbmodels.DatabaseModelBase):
'task_id', 'task_description', 'task_start_time',
'volume_id', 'deleted', 'tenant_id']
def __init__(self, task_status=None, **kwargs):
def __init__(self, task_status, **kwargs):
kwargs["task_id"] = task_status.code
kwargs["task_description"] = task_status.db_text
kwargs["deleted"] = False
@ -717,7 +714,7 @@ class InstanceServiceStatus(dbmodels.DatabaseModelBase):
_data_fields = ['instance_id', 'status_id', 'status_description']
def __init__(self, status=None, **kwargs):
def __init__(self, status, **kwargs):
kwargs["status_id"] = status.code
kwargs["status_description"] = status.description
super(InstanceServiceStatus, self).__init__(**kwargs)

View File

@ -14,19 +14,35 @@
# 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 reddwarf.common.context import ReddwarfContext
import reddwarf.extensions.mgmt.instances.models as mgmtmodels
import reddwarf.common.cfg as cfg
from reddwarf.common import exception
from reddwarf.openstack.common import log as logging
from reddwarf.openstack.common import importutils
from reddwarf.openstack.common import periodic_task
from reddwarf.taskmanager import models
from reddwarf.taskmanager.models import FreshInstanceTasks
LOG = logging.getLogger(__name__)
RPC_API_VERSION = "1.0"
CONF = cfg.CONF
class Manager(periodic_task.PeriodicTasks):
def __init__(self):
super(Manager, self).__init__()
self.admin_context = ReddwarfContext(
user=CONF.nova_proxy_admin_user,
auth_token=CONF.nova_proxy_admin_pass,
tenant=CONF.nova_proxy_admin_tenant_name)
if CONF.exists_notification_transformer:
self.exists_transformer = importutils.import_object(
CONF.exists_notification_transformer,
context=self.admin_context)
def resize_volume(self, context, instance_id, new_size):
instance_tasks = models.BuiltInstanceTasks.load(context, instance_id)
instance_tasks.resize_volume(new_size)
@ -74,3 +90,14 @@ class Manager(periodic_task.PeriodicTasks):
databases, users, service_type,
volume_size, security_groups,
backup_id)
if CONF.exists_notification_transformer:
@periodic_task.periodic_task(
ticks_between_runs=CONF.exists_notification_ticks)
def publish_exists_event(self, context):
"""
Push this in Instance Tasks to fetch a report/collection
:param context: currently None as specied in bin script
"""
mgmtmodels.publish_exist_events(self.exists_transformer,
self.admin_context)

View File

@ -0,0 +1,15 @@
# Copyright 2013 Hewlett-Packard Development Company, L.P.
# 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.
#

View File

@ -0,0 +1,293 @@
# Copyright 2013 Hewlett-Packard Development Company, L.P.
# 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 mockito import mock, when, verify, unstub, any
from testtools import TestCase
from testtools.matchers import Equals, Is, Not
from novaclient.v1_1 import Client
from novaclient.v1_1.flavors import FlavorManager, Flavor
from novaclient.v1_1.servers import Server, ServerManager
from reddwarf.backup.models import Backup
from reddwarf.common.context import ReddwarfContext
from reddwarf.db.models import DatabaseModelBase
from reddwarf.extensions.mgmt.instances.models import NotificationTransformer
from reddwarf.extensions.mgmt.instances.models import \
NovaNotificationTransformer
from reddwarf.extensions.mgmt.instances.models import SimpleMgmtInstance
from reddwarf.instance.models import DBInstance
from reddwarf.instance.models import InstanceServiceStatus
from reddwarf.instance.models import ServiceStatuses
from reddwarf.instance.tasks import InstanceTasks
import reddwarf.extensions.mgmt.instances.models as mgmtmodels
from reddwarf.openstack.common.notifier import api as notifier
from reddwarf.common import remote
class MockMgmtInstanceTest(TestCase):
def setUp(self):
super(MockMgmtInstanceTest, self).setUp()
self.context = ReddwarfContext()
self.client = mock(Client)
self.server_mgr = mock(ServerManager)
self.client.servers = self.server_mgr
self.flavor_mgr = mock(FlavorManager)
self.client.flavors = self.flavor_mgr
when(remote).create_admin_nova_client(self.context).thenReturn(
self.client)
def tearDown(self):
super(MockMgmtInstanceTest, self).tearDown()
unstub()
class TestNotificationTransformer(MockMgmtInstanceTest):
def test_tranformer(self):
transformer = NotificationTransformer(context=self.context)
status = ServiceStatuses.BUILDING.api_status
db_instance = DBInstance(InstanceTasks.BUILDING,
created='xyz',
name='test_name',
id='1',
flavor_id='flavor_1',
compute_instance_id='compute_id_1',
server_id='server_id_1',
tenant_id='tenant_id_1',
server_status=status)
when(DatabaseModelBase).find_all(deleted=False).thenReturn(
[db_instance])
when(DatabaseModelBase).find_by(instance_id='1').thenReturn(
InstanceServiceStatus(ServiceStatuses.BUILDING))
payloads = transformer()
self.assertIsNotNone(payloads)
self.assertThat(len(payloads), Equals(1))
payload = payloads[0]
self.assertThat(payload['audit-period-beginning'], Not(Is(None)))
self.assertThat(payload['audit-period-ending'], Not(Is(None)))
self.assertThat(payload['state'], Equals(status.lower()))
class TestNovaNotificationTransformer(MockMgmtInstanceTest):
def test_transformer_cache(self):
flavor = mock(Flavor)
flavor.name = 'db.small'
when(self.flavor_mgr).get('flavor_1').thenReturn(flavor)
transformer = NovaNotificationTransformer(context=self.context)
transformer2 = NovaNotificationTransformer(context=self.context)
self.assertThat(transformer._flavor_cache,
Not(Is(transformer2._flavor_cache)))
def test_lookup_flavor(self):
flavor = mock(Flavor)
flavor.name = 'flav_1'
when(self.flavor_mgr).get('1').thenReturn(flavor)
transformer = NovaNotificationTransformer(context=self.context)
self.assertThat(transformer._lookup_flavor('1'), Equals(flavor.name))
self.assertThat(transformer._lookup_flavor('2'), Equals('unknown'))
def test_tranformer(self):
status = ServiceStatuses.BUILDING.api_status
db_instance = DBInstance(InstanceTasks.BUILDING,
created='xyz',
name='test_name',
id='1',
flavor_id='flavor_1',
compute_instance_id='compute_id_1',
server_id='server_id_1',
tenant_id='tenant_id_1',
server_status=status)
server = mock(Server)
server.user_id = 'test_user_id'
mgmt_instance = SimpleMgmtInstance(self.context,
db_instance,
server,
None)
when(mgmtmodels).load_mgmt_instances(
self.context,
deleted=False,
client=self.client).thenReturn(
[mgmt_instance])
flavor = mock(Flavor)
flavor.name = 'db.small'
when(self.flavor_mgr).get('flavor_1').thenReturn(flavor)
# invocation
transformer = NovaNotificationTransformer(context=self.context)
payloads = transformer()
# assertions
self.assertIsNotNone(payloads)
self.assertThat(len(payloads), Equals(1))
payload = payloads[0]
self.assertThat(payload['audit-period-beginning'], Not(Is(None)))
self.assertThat(payload['audit-period-ending'], Not(Is(None)))
self.assertThat(payload['state'], Equals(status.lower()))
self.assertThat(payload['instance_type'], Equals('db.small'))
self.assertThat(payload['instance_type_id'], Equals('flavor_1'))
self.assertThat(payload['user_id'], Equals('test_user_id'))
def test_tranformer_shutdown_instance(self):
status = ServiceStatuses.SHUTDOWN.api_status
db_instance = DBInstance(InstanceTasks.DELETING,
created='xyz',
name='test_name',
id='1',
flavor_id='flavor_1',
compute_instance_id='compute_id_1',
server_id='server_id_1',
tenant_id='tenant_id_1',
server_status=status)
server = mock(Server)
server.user_id = 'test_user_id'
mgmt_instance = SimpleMgmtInstance(self.context,
db_instance,
server,
None)
when(Backup).running('1').thenReturn(None)
self.assertThat(mgmt_instance.status, Equals('SHUTDOWN'))
when(mgmtmodels).load_mgmt_instances(
self.context,
deleted=False,
client=self.client).thenReturn(
[mgmt_instance])
flavor = mock(Flavor)
flavor.name = 'db.small'
when(self.flavor_mgr).get('flavor_1').thenReturn(flavor)
# invocation
transformer = NovaNotificationTransformer(context=self.context)
payloads = transformer()
# assertion that SHUTDOWN instances are not reported
self.assertIsNotNone(payloads)
self.assertThat(len(payloads), Equals(0))
def test_tranformer_no_nova_instance(self):
status = ServiceStatuses.SHUTDOWN.api_status
db_instance = DBInstance(InstanceTasks.DELETING,
created='xyz',
name='test_name',
id='1',
flavor_id='flavor_1',
compute_instance_id='compute_id_1',
server_id='server_id_1',
tenant_id='tenant_id_1',
server_status=status)
mgmt_instance = SimpleMgmtInstance(self.context,
db_instance,
None,
None)
when(Backup).running('1').thenReturn(None)
self.assertThat(mgmt_instance.status, Equals('SHUTDOWN'))
when(mgmtmodels).load_mgmt_instances(
self.context,
deleted=False,
client=self.client).thenReturn(
[mgmt_instance])
flavor = mock(Flavor)
flavor.name = 'db.small'
when(self.flavor_mgr).get('flavor_1').thenReturn(flavor)
# invocation
transformer = NovaNotificationTransformer(context=self.context)
payloads = transformer()
# assertion that SHUTDOWN instances are not reported
self.assertIsNotNone(payloads)
self.assertThat(len(payloads), Equals(0))
def test_tranformer_flavor_cache(self):
status = ServiceStatuses.BUILDING.api_status
db_instance = DBInstance(InstanceTasks.BUILDING,
created='xyz',
name='test_name',
id='1',
flavor_id='flavor_1',
compute_instance_id='compute_id_1',
server_id='server_id_1',
tenant_id='tenant_id_1',
server_status=status)
server = mock(Server)
server.user_id = 'test_user_id'
mgmt_instance = SimpleMgmtInstance(self.context,
db_instance,
server,
None)
when(mgmtmodels).load_mgmt_instances(
self.context,
deleted=False,
client=self.client).thenReturn(
[mgmt_instance])
flavor = mock(Flavor)
flavor.name = 'db.small'
when(self.flavor_mgr).get('flavor_1').thenReturn(flavor)
transformer = NovaNotificationTransformer(context=self.context)
transformer()
# call twice ensure client.flavor invoked once
payloads = transformer()
self.assertIsNotNone(payloads)
self.assertThat(len(payloads), Equals(1))
payload = payloads[0]
self.assertThat(payload['audit-period-beginning'], Not(Is(None)))
self.assertThat(payload['audit-period-ending'], Not(Is(None)))
self.assertThat(payload['state'], Equals(status.lower()))
self.assertThat(payload['instance_type'], Equals('db.small'))
self.assertThat(payload['instance_type_id'], Equals('flavor_1'))
self.assertThat(payload['user_id'], Equals('test_user_id'))
# ensure cache was used to get flavor second time
verify(self.flavor_mgr).get('flavor_1')
class TestMgmtInstanceTasks(MockMgmtInstanceTest):
def test_public_exists_events(self):
status = ServiceStatuses.BUILDING.api_status
db_instance = DBInstance(InstanceTasks.BUILDING,
created='xyz',
name='test_name',
id='1',
flavor_id='flavor_1',
compute_instance_id='compute_id_1',
server_id='server_id_1',
tenant_id='tenant_id_1',
server_status=status)
server = mock(Server)
server.user_id = 'test_user_id'
mgmt_instance = SimpleMgmtInstance(self.context,
db_instance,
server,
None)
when(mgmtmodels).load_mgmt_instances(
self.context,
deleted=False,
client=self.client).thenReturn(
[mgmt_instance, mgmt_instance])
flavor = mock(Flavor)
flavor.name = 'db.small'
when(self.flavor_mgr).get('flavor_1').thenReturn(flavor)
when(notifier).notify(self.context,
any(str),
'reddwarf.instance.exists',
'INFO',
any(dict)).thenReturn(None)
# invocation
mgmtmodels.publish_exist_events(
NovaNotificationTransformer(context=self.context), self.context)
# assertion
verify(notifier, times=2).notify(self.context,
any(str),
'reddwarf.instance.exists',
'INFO',
any(dict))