Adding Exists Event Publishing
TaskManager periodically sends notifications for active instances Implements: blueprint notification-exists-event Change-Id: If9ec0a6b89ee28c550297e5a7450b0b927e034f2
This commit is contained in:
parent
d1c996b7d1
commit
3a276f77c3
@ -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_
|
||||
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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]
|
||||
|
@ -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")
|
||||
]
|
||||
|
||||
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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)
|
||||
|
@ -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)
|
||||
|
15
reddwarf/tests/unittests/mgmt/__init__.py
Normal file
15
reddwarf/tests/unittests/mgmt/__init__.py
Normal 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.
|
||||
#
|
293
reddwarf/tests/unittests/mgmt/test_models.py
Normal file
293
reddwarf/tests/unittests/mgmt/test_models.py
Normal 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))
|
Loading…
Reference in New Issue
Block a user