Support versioned notifications
Support nova versioned notifications. Unversioned notifications are still supported and the default. The CI is configured to test versioned notifications, and both implementations use the same methods. Because of this, testing versioned notifications also covers unversioned notifications, since the execution path flows through both. Change-Id: If028afa9e9fbcb344786cd287605e0d9af5d3c01
This commit is contained in:
parent
4d997dddc6
commit
609f6e2b2b
23
README.rst
23
README.rst
|
@ -117,6 +117,8 @@ novajoin REST service and enable notifications in
|
||||||
|
|
||||||
.. note::
|
.. note::
|
||||||
Notifications have to be also enabled and configured on nova computes!
|
Notifications have to be also enabled and configured on nova computes!
|
||||||
|
See also information about enabling versioned and neutron notifications
|
||||||
|
in the `Notification listener Configuration`_ section below.
|
||||||
|
|
||||||
Novajoin enables keystone authentication by default, as seen in
|
Novajoin enables keystone authentication by default, as seen in
|
||||||
**/etc/novajoin/join-api-paste.ini**. So credentials need to be set for
|
**/etc/novajoin/join-api-paste.ini**. So credentials need to be set for
|
||||||
|
@ -217,14 +219,27 @@ send notifications to the novajoin topic in /etc/nova/nova.conf::
|
||||||
|
|
||||||
[oslo_messaging_notifications]
|
[oslo_messaging_notifications]
|
||||||
...
|
...
|
||||||
topics=notifications,novajoin_notifications
|
topics = notifications,novajoin_notifications
|
||||||
|
|
||||||
|
In case of versioned notifications the configuration will look differently::
|
||||||
|
|
||||||
|
[notifications]
|
||||||
|
notify_on_state_change = vm_state
|
||||||
|
notification_format = versioned
|
||||||
|
versioned_notifications_topics = versioned_notifications,novajoin_notifications
|
||||||
|
|
||||||
.. note::
|
.. note::
|
||||||
Notifications have to be also enabled and configured on nova computes!
|
Notifications have to be also enabled and configured on nova computes!
|
||||||
|
|
||||||
If you simply use notifications and ceilometer is running then the
|
To enable neutron notifications, in /etc/neutron/neutron.conf::
|
||||||
notifications will be roughly split between the two services in a
|
|
||||||
round-robin format.
|
[oslo_messaging_notifications]
|
||||||
|
driver = messagingv2
|
||||||
|
topics = notifications,novajoin_notifications
|
||||||
|
|
||||||
|
If you use notifications without changing the topic and ceilometer is
|
||||||
|
running, then the notifications will be roughly split between the two
|
||||||
|
services in a round-robin fashion.
|
||||||
|
|
||||||
Usage
|
Usage
|
||||||
=====
|
=====
|
||||||
|
|
|
@ -51,6 +51,14 @@ service_opts = [
|
||||||
help='Number retries when downloading an image from glance'),
|
help='Number retries when downloading an image from glance'),
|
||||||
cfg.StrOpt('auth_strategy', default='keystone',
|
cfg.StrOpt('auth_strategy', default='keystone',
|
||||||
help='Strategy to use for authentication.'),
|
help='Strategy to use for authentication.'),
|
||||||
|
cfg.StrOpt('notification_format', default='unversioned',
|
||||||
|
choices=[
|
||||||
|
('versioned',
|
||||||
|
'Only the new versioned notifications are read'),
|
||||||
|
('unversioned',
|
||||||
|
'Only the legacy unversioned notifications are read'),
|
||||||
|
],
|
||||||
|
help='The format of notifications to read.'),
|
||||||
cfg.StrOpt('notifications_topic', default='novajoin_notifications',
|
cfg.StrOpt('notifications_topic', default='novajoin_notifications',
|
||||||
help='Topic on which to listen to notifications.'),
|
help='Topic on which to listen to notifications.'),
|
||||||
]
|
]
|
||||||
|
|
|
@ -151,3 +151,9 @@ class ImageNotFound(NotFound):
|
||||||
|
|
||||||
class PolicyNotAuthorized(NotAuthorized):
|
class PolicyNotAuthorized(NotAuthorized):
|
||||||
message = "Policy doesn't allow %(action)s to be performed."
|
message = "Policy doesn't allow %(action)s to be performed."
|
||||||
|
|
||||||
|
|
||||||
|
class NotificationVersionMismatch(JoinException):
|
||||||
|
message = ("Provided notification version "
|
||||||
|
"%(provided_maj)s.%(provided_min)s did not match expected "
|
||||||
|
"%(expected_maj)s.%(expected_min)s for %(type)s")
|
||||||
|
|
|
@ -21,9 +21,11 @@ import json
|
||||||
import sys
|
import sys
|
||||||
import time
|
import time
|
||||||
|
|
||||||
|
import glanceclient as glance_client
|
||||||
from neutronclient.v2_0 import client as neutron_client
|
from neutronclient.v2_0 import client as neutron_client
|
||||||
from novaclient import client as nova_client
|
from novaclient import client as nova_client
|
||||||
from novajoin import config
|
from novajoin import config
|
||||||
|
from novajoin import exception
|
||||||
from novajoin.ipa import IPAClient
|
from novajoin.ipa import IPAClient
|
||||||
from novajoin import join
|
from novajoin import join
|
||||||
from novajoin.keystone_client import get_session
|
from novajoin.keystone_client import get_session
|
||||||
|
@ -57,12 +59,53 @@ def neutronclient():
|
||||||
return neutron_client.Client(session=session)
|
return neutron_client.Client(session=session)
|
||||||
|
|
||||||
|
|
||||||
|
def glanceclient():
|
||||||
|
session = get_session()
|
||||||
|
return glance_client.Client('2', session=session)
|
||||||
|
|
||||||
|
|
||||||
class Registry(dict):
|
class Registry(dict):
|
||||||
def __call__(self, name):
|
def __call__(self, name, version=None, service='nova'):
|
||||||
def decorator(fun):
|
def register_event(fun):
|
||||||
|
if version:
|
||||||
|
def check_event(sself, payload):
|
||||||
|
self.check_version(payload, version, service)
|
||||||
|
return fun(sself, payload[service + '_object.data'])
|
||||||
|
self[name] = check_event
|
||||||
|
return check_event
|
||||||
self[name] = fun
|
self[name] = fun
|
||||||
return fun
|
return fun
|
||||||
return decorator
|
return register_event
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def check_version(payload, expected_version, service):
|
||||||
|
"""Check nova notification version
|
||||||
|
|
||||||
|
If actual's major version is different from expected, a
|
||||||
|
NotificationVersionMismatch error is raised.
|
||||||
|
If the minor versions are different, a DEBUG level log
|
||||||
|
message is output
|
||||||
|
"""
|
||||||
|
notification_version = payload[service + '_object.version']
|
||||||
|
notification_name = payload[service + '_object.name']
|
||||||
|
|
||||||
|
maj_ver, min_ver = map(int, notification_version.split('.'))
|
||||||
|
expected_maj, expected_min = map(int, expected_version.split('.'))
|
||||||
|
if maj_ver != expected_maj:
|
||||||
|
raise exception.NotificationVersionMismatch(
|
||||||
|
provided_maj=maj_ver, provided_min=min_ver,
|
||||||
|
expected_maj=expected_maj, expected_min=expected_min,
|
||||||
|
type=notification_name)
|
||||||
|
|
||||||
|
if min_ver != expected_min:
|
||||||
|
LOG.debug(
|
||||||
|
"Notification %(type)s minor version mismatch, "
|
||||||
|
"provided: %(provided_maj)s.%(provided_min)s, "
|
||||||
|
"expected: %(expected_maj)s.%(expected_min)s.",
|
||||||
|
{"type": notification_name,
|
||||||
|
"provided_maj": maj_ver, "provided_min": min_ver,
|
||||||
|
"expected_maj": expected_maj, "expected_min": expected_min}
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
class NotificationEndpoint(object):
|
class NotificationEndpoint(object):
|
||||||
|
@ -90,19 +133,19 @@ class NotificationEndpoint(object):
|
||||||
event_handler(self, payload)
|
event_handler(self, payload)
|
||||||
|
|
||||||
@event_handlers('compute.instance.create.end')
|
@event_handlers('compute.instance.create.end')
|
||||||
def instance_create(self, payload):
|
def compute_instance_create(self, payload):
|
||||||
hostname = self._generate_hostname(payload.get('hostname'))
|
hostname = self._generate_hostname(payload.get('hostname'))
|
||||||
instance_id = payload.get('instance_id')
|
instance_id = payload['instance_id']
|
||||||
LOG.info("Add new host %s (%s)", instance_id, hostname)
|
LOG.info("Add new host %s (%s)", instance_id, hostname)
|
||||||
|
|
||||||
@event_handlers('compute.instance.update')
|
@event_handlers('compute.instance.update')
|
||||||
def instance_update(self, payload):
|
def compute_instance_update(self, payload):
|
||||||
ipa = ipaclient()
|
ipa = ipaclient()
|
||||||
join_controller = join.JoinController(ipa)
|
join_controller = join.JoinController(ipa)
|
||||||
hostname_short = payload.get('hostname')
|
hostname_short = payload['hostname']
|
||||||
instance_id = payload.get('instance_id')
|
instance_id = payload['instance_id']
|
||||||
payload_metadata = payload.get('metadata')
|
payload_metadata = payload['metadata']
|
||||||
image_metadata = payload.get('image_meta')
|
image_metadata = payload['image_meta']
|
||||||
|
|
||||||
hostname = self._generate_hostname(hostname_short)
|
hostname = self._generate_hostname(hostname_short)
|
||||||
|
|
||||||
|
@ -133,11 +176,11 @@ class NotificationEndpoint(object):
|
||||||
ipa.flush_batch_operation()
|
ipa.flush_batch_operation()
|
||||||
|
|
||||||
@event_handlers('compute.instance.delete.end')
|
@event_handlers('compute.instance.delete.end')
|
||||||
def instance_delete(self, payload):
|
def compute_instance_delete(self, payload):
|
||||||
hostname_short = payload.get('hostname')
|
hostname_short = payload['hostname']
|
||||||
instance_id = payload.get('instance_id')
|
instance_id = payload['instance_id']
|
||||||
payload_metadata = payload.get('metadata')
|
payload_metadata = payload['metadata']
|
||||||
image_metadata = payload.get('image_meta')
|
image_metadata = payload['image_meta']
|
||||||
|
|
||||||
hostname = self._generate_hostname(hostname_short)
|
hostname = self._generate_hostname(hostname_short)
|
||||||
|
|
||||||
|
@ -156,20 +199,20 @@ class NotificationEndpoint(object):
|
||||||
|
|
||||||
@event_handlers('network.floating_ip.associate')
|
@event_handlers('network.floating_ip.associate')
|
||||||
def floaitng_ip_associate(self, payload):
|
def floaitng_ip_associate(self, payload):
|
||||||
floating_ip = payload.get('floating_ip')
|
floating_ip = payload['floating_ip']
|
||||||
LOG.info("Associate floating IP %s" % floating_ip)
|
LOG.info("Associate floating IP %s" % floating_ip)
|
||||||
ipa = ipaclient()
|
ipa = ipaclient()
|
||||||
nova = novaclient()
|
nova = novaclient()
|
||||||
server = nova.servers.get(payload.get('instance_id'))
|
server = nova.servers.get(payload['instance_id'])
|
||||||
if server:
|
if server:
|
||||||
ipa.add_ip(server.get, floating_ip)
|
ipa.add_ip(server.name, floating_ip)
|
||||||
else:
|
else:
|
||||||
LOG.error("Could not resolve %s into a hostname",
|
LOG.error("Could not resolve %s into a hostname",
|
||||||
payload.get('instance_id'))
|
payload['instance_id'])
|
||||||
|
|
||||||
@event_handlers('network.floating_ip.disassociate')
|
@event_handlers('network.floating_ip.disassociate')
|
||||||
def floating_ip_disassociate(self, payload):
|
def floating_ip_disassociate(self, payload):
|
||||||
floating_ip = payload.get('floating_ip')
|
floating_ip = payload['floating_ip']
|
||||||
LOG.info("Disassociate floating IP %s" % floating_ip)
|
LOG.info("Disassociate floating IP %s" % floating_ip)
|
||||||
ipa = ipaclient()
|
ipa = ipaclient()
|
||||||
ipa.remove_ip(floating_ip)
|
ipa.remove_ip(floating_ip)
|
||||||
|
@ -177,9 +220,9 @@ class NotificationEndpoint(object):
|
||||||
@event_handlers('floatingip.update.end')
|
@event_handlers('floatingip.update.end')
|
||||||
def floating_ip_update(self, payload):
|
def floating_ip_update(self, payload):
|
||||||
"""Neutron event"""
|
"""Neutron event"""
|
||||||
floatingip = payload.get('floatingip')
|
floatingip = payload['floatingip']
|
||||||
floating_ip = floatingip.get('floating_ip_address')
|
floating_ip = floatingip['floating_ip_address']
|
||||||
port_id = floatingip.get('port_id')
|
port_id = floatingip['port_id']
|
||||||
ipa = ipaclient()
|
ipa = ipaclient()
|
||||||
if port_id:
|
if port_id:
|
||||||
LOG.info("Neutron floating IP associate: %s" % floating_ip)
|
LOG.info("Neutron floating IP associate: %s" % floating_ip)
|
||||||
|
@ -295,6 +338,48 @@ class NotificationEndpoint(object):
|
||||||
return host
|
return host
|
||||||
|
|
||||||
|
|
||||||
|
class VersionedNotificationEndpoint(NotificationEndpoint):
|
||||||
|
|
||||||
|
filter_rule = oslo_messaging.notify.filter.NotificationFilter(
|
||||||
|
publisher_id='^nova-compute.*|^network.*',
|
||||||
|
event_type='^instance.create.end|'
|
||||||
|
'^instance.delete.end|'
|
||||||
|
'^instance.update|'
|
||||||
|
'^floatingip.update.end')
|
||||||
|
|
||||||
|
event_handlers = Registry(NotificationEndpoint.event_handlers)
|
||||||
|
|
||||||
|
@event_handlers('instance.create.end', '1.10')
|
||||||
|
def instance_create(self, payload):
|
||||||
|
newpayload = {
|
||||||
|
'hostname': payload['host_name'],
|
||||||
|
'instance_id': payload['uuid'],
|
||||||
|
}
|
||||||
|
self.compute_instance_create(newpayload)
|
||||||
|
|
||||||
|
@event_handlers('instance.update', '1.8')
|
||||||
|
def instance_update(self, payload):
|
||||||
|
glance = glanceclient()
|
||||||
|
newpayload = {
|
||||||
|
'hostname': payload['host_name'],
|
||||||
|
'instance_id': payload['uuid'],
|
||||||
|
'metadata': payload['metadata'],
|
||||||
|
'image_meta': glance.images.get(payload['image_uuid'])
|
||||||
|
}
|
||||||
|
self.compute_instance_update(newpayload)
|
||||||
|
|
||||||
|
@event_handlers('instance.delete.end', '1.7')
|
||||||
|
def instance_delete(self, payload):
|
||||||
|
glance = glanceclient()
|
||||||
|
newpayload = {
|
||||||
|
'hostname': payload['host_name'],
|
||||||
|
'instance_id': payload['uuid'],
|
||||||
|
'metadata': payload['metadata'],
|
||||||
|
'image_meta': glance.images.get(payload['image_uuid'])
|
||||||
|
}
|
||||||
|
self.compute_instance_delete(newpayload)
|
||||||
|
|
||||||
|
|
||||||
def main():
|
def main():
|
||||||
register_keystoneauth_opts(CONF)
|
register_keystoneauth_opts(CONF)
|
||||||
CONF(sys.argv[1:], version='1.0.21',
|
CONF(sys.argv[1:], version='1.0.21',
|
||||||
|
@ -303,7 +388,10 @@ def main():
|
||||||
|
|
||||||
transport = oslo_messaging.get_notification_transport(CONF)
|
transport = oslo_messaging.get_notification_transport(CONF)
|
||||||
targets = [oslo_messaging.Target(topic=CONF.notifications_topic)]
|
targets = [oslo_messaging.Target(topic=CONF.notifications_topic)]
|
||||||
endpoints = [NotificationEndpoint()]
|
if CONF.notification_format == 'unversioned':
|
||||||
|
endpoints = [NotificationEndpoint()]
|
||||||
|
elif CONF.notification_format == 'versioned':
|
||||||
|
endpoints = [VersionedNotificationEndpoint()]
|
||||||
|
|
||||||
server = oslo_messaging.get_notification_listener(transport,
|
server = oslo_messaging.get_notification_listener(transport,
|
||||||
targets,
|
targets,
|
||||||
|
|
|
@ -0,0 +1,18 @@
|
||||||
|
{
|
||||||
|
"priority" : "INFO",
|
||||||
|
"message_id" : "281218d3-0764-4397-b844-936c93fb89e6",
|
||||||
|
"event_type" : "floatingip.update.end",
|
||||||
|
"timestamp" : "2012-11-18 01:29:29.497899",
|
||||||
|
"payload" : {
|
||||||
|
"floatingip" : {
|
||||||
|
"floating_network_id" : "d9edfcd5-f245-4f45-be26-4383942fd74c",
|
||||||
|
"tenant_id" : "c97027dd880d4c129ae7a4ba7edade05",
|
||||||
|
"fixed_ip_address" : "172.16.59.10",
|
||||||
|
"router_id" : "62c1fd2b-8149-4222-8d6b-e581c55e5264",
|
||||||
|
"port_id" : "289ed46b-274c-444d-9fd4-bddf8acc7d7c",
|
||||||
|
"floating_ip_address" : "192.168.5.201",
|
||||||
|
"id" : "f38ff2b6-cd4d-433e-8a9c-9e00dfc05b1e"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"publisher_id" : "network.svc02.os.lan"
|
||||||
|
}
|
|
@ -0,0 +1,18 @@
|
||||||
|
{
|
||||||
|
"priority" : "INFO",
|
||||||
|
"message_id" : "e9667b80-d2dc-4687-b2c6-2e648520157c",
|
||||||
|
"event_type" : "floatingip.update.end",
|
||||||
|
"timestamp" : "2012-11-18 01:35:08.312766",
|
||||||
|
"payload" : {
|
||||||
|
"floatingip" : {
|
||||||
|
"floating_network_id" : "d9edfcd5-f245-4f45-be26-4383942fd74c",
|
||||||
|
"tenant_id" : "c97027dd880d4c129ae7a4ba7edade05",
|
||||||
|
"fixed_ip_address" : null,
|
||||||
|
"router_id" : null,
|
||||||
|
"port_id" : null,
|
||||||
|
"floating_ip_address" : "192.168.5.201",
|
||||||
|
"id" : "f38ff2b6-cd4d-433e-8a9c-9e00dfc05b1e"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"publisher_id" : "network.svc02.os.lan"
|
||||||
|
}
|
|
@ -0,0 +1,105 @@
|
||||||
|
{
|
||||||
|
"event_type": "instance.create.end",
|
||||||
|
"payload": {
|
||||||
|
"nova_object.data": {
|
||||||
|
"action_initiator_project": "6f70656e737461636b20342065766572",
|
||||||
|
"action_initiator_user": "fake",
|
||||||
|
"architecture": "x86_64",
|
||||||
|
"auto_disk_config": "MANUAL",
|
||||||
|
"availability_zone": "nova",
|
||||||
|
"block_devices": [],
|
||||||
|
"created_at": "2012-10-29T13:42:11Z",
|
||||||
|
"deleted_at": null,
|
||||||
|
"display_description": "some-server",
|
||||||
|
"display_name": "some-server",
|
||||||
|
"fault": null,
|
||||||
|
"flavor": {
|
||||||
|
"nova_object.data": {
|
||||||
|
"description": null,
|
||||||
|
"disabled": false,
|
||||||
|
"ephemeral_gb": 0,
|
||||||
|
"extra_specs": {
|
||||||
|
"hw:watchdog_action": "disabled"
|
||||||
|
},
|
||||||
|
"flavorid": "a22d5517-147c-4147-a0d1-e698df5cd4e3",
|
||||||
|
"is_public": true,
|
||||||
|
"memory_mb": 512,
|
||||||
|
"name": "test_flavor",
|
||||||
|
"projects": null,
|
||||||
|
"root_gb": 1,
|
||||||
|
"rxtx_factor": 1.0,
|
||||||
|
"swap": 0,
|
||||||
|
"vcpu_weight": 0,
|
||||||
|
"vcpus": 1
|
||||||
|
},
|
||||||
|
"nova_object.name": "FlavorPayload",
|
||||||
|
"nova_object.namespace": "nova",
|
||||||
|
"nova_object.version": "1.4"
|
||||||
|
},
|
||||||
|
"host": "compute",
|
||||||
|
"host_name": "some-server",
|
||||||
|
"image_uuid": "155d900f-4e14-4e4c-a73d-069cbf4541e6",
|
||||||
|
"ip_addresses": [
|
||||||
|
{
|
||||||
|
"nova_object.data": {
|
||||||
|
"address": "192.168.1.3",
|
||||||
|
"device_name": "tapce531f90-19",
|
||||||
|
"label": "private-network",
|
||||||
|
"mac": "fa:16:3e:4c:2c:30",
|
||||||
|
"meta": {},
|
||||||
|
"port_uuid": "ce531f90-199f-48c0-816c-13e38010b442",
|
||||||
|
"version": 4
|
||||||
|
},
|
||||||
|
"nova_object.name": "IpPayload",
|
||||||
|
"nova_object.namespace": "nova",
|
||||||
|
"nova_object.version": "1.0"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"kernel_id": "",
|
||||||
|
"key_name": "my-key",
|
||||||
|
"keypairs": [
|
||||||
|
{
|
||||||
|
"nova_object.data": {
|
||||||
|
"fingerprint": "1e:2c:9b:56:79:4b:45:77:f9:ca:7a:98:2c:b0:d5:3c",
|
||||||
|
"name": "my-key",
|
||||||
|
"public_key": "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAAAgQDx8nkQv/zgGgB4rMYmIf+6A4l6Rr+o/6lHBQdW5aYd44bd8JttDCE/F/pNRr0lRE+PiqSPO8nDPHw0010JeMH9gYgnnFlyY3/OcJ02RhIPyyxYpv9FhY+2YiUkpwFOcLImyrxEsYXpD/0d3ac30bNH6Sw9JD9UZHYcpSxsIbECHw== Generated-by-Nova",
|
||||||
|
"type": "ssh",
|
||||||
|
"user_id": "fake"
|
||||||
|
},
|
||||||
|
"nova_object.name": "KeypairPayload",
|
||||||
|
"nova_object.namespace": "nova",
|
||||||
|
"nova_object.version": "1.0"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"launched_at": "2012-10-29T13:42:11Z",
|
||||||
|
"locked": false,
|
||||||
|
"metadata": {},
|
||||||
|
"node": "fake-mini",
|
||||||
|
"os_type": null,
|
||||||
|
"power_state": "running",
|
||||||
|
"progress": 0,
|
||||||
|
"ramdisk_id": "",
|
||||||
|
"request_id": "req-5b6c791d-5709-4f36-8fbe-c3e02869e35d",
|
||||||
|
"reservation_id": "r-npxv0e40",
|
||||||
|
"state": "active",
|
||||||
|
"tags": [
|
||||||
|
"tag"
|
||||||
|
],
|
||||||
|
"task_state": null,
|
||||||
|
"tenant_id": "6f70656e737461636b20342065766572",
|
||||||
|
"terminated_at": null,
|
||||||
|
"trusted_image_certificates": [
|
||||||
|
"cert-id-1",
|
||||||
|
"cert-id-2"
|
||||||
|
],
|
||||||
|
"updated_at": "2012-10-29T13:42:11Z",
|
||||||
|
"user_id": "fake",
|
||||||
|
"uuid": "178b0921-8f85-4257-88b6-2e743b5a975c"
|
||||||
|
},
|
||||||
|
"nova_object.name": "InstanceCreatePayload",
|
||||||
|
"nova_object.namespace": "nova",
|
||||||
|
"nova_object.version": "1.10"
|
||||||
|
},
|
||||||
|
"priority": "INFO",
|
||||||
|
"publisher_id": "nova-compute:compute"
|
||||||
|
}
|
|
@ -0,0 +1,82 @@
|
||||||
|
{
|
||||||
|
"event_type": "instance.delete.end",
|
||||||
|
"payload": {
|
||||||
|
"nova_object.data": {
|
||||||
|
"action_initiator_project": "6f70656e737461636b20342065766572",
|
||||||
|
"action_initiator_user": "fake",
|
||||||
|
"architecture": "x86_64",
|
||||||
|
"auto_disk_config": "MANUAL",
|
||||||
|
"availability_zone": "nova",
|
||||||
|
"block_devices": [
|
||||||
|
{
|
||||||
|
"nova_object.data": {
|
||||||
|
"boot_index": null,
|
||||||
|
"delete_on_termination": false,
|
||||||
|
"device_name": "/dev/sdb",
|
||||||
|
"tag": null,
|
||||||
|
"volume_id": "a07f71dc-8151-4e7d-a0cc-cd24a3f11113"
|
||||||
|
},
|
||||||
|
"nova_object.name": "BlockDevicePayload",
|
||||||
|
"nova_object.namespace": "nova",
|
||||||
|
"nova_object.version": "1.0"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"created_at": "2012-10-29T13:42:11Z",
|
||||||
|
"deleted_at": "2012-10-29T13:42:11Z",
|
||||||
|
"display_description": "some-server",
|
||||||
|
"display_name": "some-server",
|
||||||
|
"fault": null,
|
||||||
|
"flavor": {
|
||||||
|
"nova_object.data": {
|
||||||
|
"description": null,
|
||||||
|
"disabled": false,
|
||||||
|
"ephemeral_gb": 0,
|
||||||
|
"extra_specs": {
|
||||||
|
"hw:watchdog_action": "disabled"
|
||||||
|
},
|
||||||
|
"flavorid": "a22d5517-147c-4147-a0d1-e698df5cd4e3",
|
||||||
|
"is_public": true,
|
||||||
|
"memory_mb": 512,
|
||||||
|
"name": "test_flavor",
|
||||||
|
"projects": null,
|
||||||
|
"root_gb": 1,
|
||||||
|
"rxtx_factor": 1.0,
|
||||||
|
"swap": 0,
|
||||||
|
"vcpu_weight": 0,
|
||||||
|
"vcpus": 1
|
||||||
|
},
|
||||||
|
"nova_object.name": "FlavorPayload",
|
||||||
|
"nova_object.namespace": "nova",
|
||||||
|
"nova_object.version": "1.4"
|
||||||
|
},
|
||||||
|
"host": "compute",
|
||||||
|
"host_name": "some-server",
|
||||||
|
"image_uuid": "155d900f-4e14-4e4c-a73d-069cbf4541e6",
|
||||||
|
"ip_addresses": [],
|
||||||
|
"kernel_id": "",
|
||||||
|
"key_name": "my-key",
|
||||||
|
"launched_at": "2012-10-29T13:42:11Z",
|
||||||
|
"locked": false,
|
||||||
|
"metadata": {},
|
||||||
|
"node": "fake-mini",
|
||||||
|
"os_type": null,
|
||||||
|
"power_state": "pending",
|
||||||
|
"progress": 0,
|
||||||
|
"ramdisk_id": "",
|
||||||
|
"request_id": "req-5b6c791d-5709-4f36-8fbe-c3e02869e35d",
|
||||||
|
"reservation_id": "r-npxv0e40",
|
||||||
|
"state": "deleted",
|
||||||
|
"task_state": null,
|
||||||
|
"tenant_id": "6f70656e737461636b20342065766572",
|
||||||
|
"terminated_at": "2012-10-29T13:42:11Z",
|
||||||
|
"updated_at": "2012-10-29T13:42:11Z",
|
||||||
|
"user_id": "fake",
|
||||||
|
"uuid": "178b0921-8f85-4257-88b6-2e743b5a975c"
|
||||||
|
},
|
||||||
|
"nova_object.name": "InstanceActionPayload",
|
||||||
|
"nova_object.namespace": "nova",
|
||||||
|
"nova_object.version": "1.7"
|
||||||
|
},
|
||||||
|
"priority": "INFO",
|
||||||
|
"publisher_id": "nova-compute:compute"
|
||||||
|
}
|
|
@ -0,0 +1,91 @@
|
||||||
|
{
|
||||||
|
"event_type": "instance.update",
|
||||||
|
"payload": {
|
||||||
|
"nova_object.data": {
|
||||||
|
"action_initiator_project": "6f70656e737461636b20342065766572",
|
||||||
|
"action_initiator_user": "fake",
|
||||||
|
"architecture": "x86_64",
|
||||||
|
"audit_period": {
|
||||||
|
"nova_object.data": {
|
||||||
|
"audit_period_beginning": "2012-10-01T00:00:00Z",
|
||||||
|
"audit_period_ending": "2012-10-29T13:42:11Z"
|
||||||
|
},
|
||||||
|
"nova_object.name": "AuditPeriodPayload",
|
||||||
|
"nova_object.namespace": "nova",
|
||||||
|
"nova_object.version": "1.0"
|
||||||
|
},
|
||||||
|
"auto_disk_config": "MANUAL",
|
||||||
|
"availability_zone": "nova",
|
||||||
|
"bandwidth": [],
|
||||||
|
"block_devices": [],
|
||||||
|
"created_at": "2012-10-29T13:42:11Z",
|
||||||
|
"deleted_at": null,
|
||||||
|
"display_description": "some-server",
|
||||||
|
"display_name": "some-server",
|
||||||
|
"flavor": {
|
||||||
|
"nova_object.data": {
|
||||||
|
"description": null,
|
||||||
|
"disabled": false,
|
||||||
|
"ephemeral_gb": 0,
|
||||||
|
"extra_specs": {
|
||||||
|
"hw:watchdog_action": "disabled"
|
||||||
|
},
|
||||||
|
"flavorid": "a22d5517-147c-4147-a0d1-e698df5cd4e3",
|
||||||
|
"is_public": true,
|
||||||
|
"memory_mb": 512,
|
||||||
|
"name": "test_flavor",
|
||||||
|
"projects": null,
|
||||||
|
"root_gb": 1,
|
||||||
|
"rxtx_factor": 1.0,
|
||||||
|
"swap": 0,
|
||||||
|
"vcpu_weight": 0,
|
||||||
|
"vcpus": 1
|
||||||
|
},
|
||||||
|
"nova_object.name": "FlavorPayload",
|
||||||
|
"nova_object.namespace": "nova",
|
||||||
|
"nova_object.version": "1.4"
|
||||||
|
},
|
||||||
|
"host": "compute",
|
||||||
|
"host_name": "some-server",
|
||||||
|
"image_uuid": "155d900f-4e14-4e4c-a73d-069cbf4541e6",
|
||||||
|
"ip_addresses": [],
|
||||||
|
"kernel_id": "",
|
||||||
|
"key_name": "my-key",
|
||||||
|
"launched_at": null,
|
||||||
|
"locked": false,
|
||||||
|
"metadata": {},
|
||||||
|
"node": "fake-mini",
|
||||||
|
"old_display_name": null,
|
||||||
|
"os_type": null,
|
||||||
|
"power_state": "pending",
|
||||||
|
"progress": 0,
|
||||||
|
"ramdisk_id": "",
|
||||||
|
"request_id": "req-5b6c791d-5709-4f36-8fbe-c3e02869e35d",
|
||||||
|
"reservation_id": "r-npxv0e40",
|
||||||
|
"state": "active",
|
||||||
|
"state_update": {
|
||||||
|
"nova_object.data": {
|
||||||
|
"new_task_state": null,
|
||||||
|
"old_state": "building",
|
||||||
|
"old_task_state": null,
|
||||||
|
"state": "building"
|
||||||
|
},
|
||||||
|
"nova_object.name": "InstanceStateUpdatePayload",
|
||||||
|
"nova_object.namespace": "nova",
|
||||||
|
"nova_object.version": "1.0"
|
||||||
|
},
|
||||||
|
"tags": [],
|
||||||
|
"task_state": "scheduling",
|
||||||
|
"tenant_id": "6f70656e737461636b20342065766572",
|
||||||
|
"terminated_at": null,
|
||||||
|
"updated_at": null,
|
||||||
|
"user_id": "fake",
|
||||||
|
"uuid": "178b0921-8f85-4257-88b6-2e743b5a975c"
|
||||||
|
},
|
||||||
|
"nova_object.name": "InstanceUpdatePayload",
|
||||||
|
"nova_object.namespace": "nova",
|
||||||
|
"nova_object.version": "1.8"
|
||||||
|
},
|
||||||
|
"priority": "INFO",
|
||||||
|
"publisher_id": "nova-compute:fake-mini"
|
||||||
|
}
|
|
@ -0,0 +1,94 @@
|
||||||
|
# Copyright 2018 Red Hat, Inc.
|
||||||
|
#
|
||||||
|
# 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.
|
||||||
|
|
||||||
|
import mock
|
||||||
|
import os
|
||||||
|
|
||||||
|
from oslo_messaging.notify import dispatcher as notify_dispatcher
|
||||||
|
from oslo_messaging.notify import NotificationResult
|
||||||
|
from oslo_serialization import jsonutils
|
||||||
|
|
||||||
|
from novajoin import notifications
|
||||||
|
from novajoin import test
|
||||||
|
|
||||||
|
|
||||||
|
SAMPLES_DIR = os.path.dirname(os.path.realpath(__file__))
|
||||||
|
|
||||||
|
|
||||||
|
class NotificationFormatsTest(test.TestCase):
|
||||||
|
|
||||||
|
def _get_event(self, filename):
|
||||||
|
json_sample = os.path.join(SAMPLES_DIR, filename)
|
||||||
|
with open(json_sample) as sample_file:
|
||||||
|
return jsonutils.loads(sample_file.read())
|
||||||
|
|
||||||
|
def _run_dispatcher(self, event):
|
||||||
|
dispatcher = notify_dispatcher.NotificationDispatcher(
|
||||||
|
[notifications.VersionedNotificationEndpoint()], None)
|
||||||
|
return dispatcher.dispatch(mock.Mock(ctxt={}, message=event))
|
||||||
|
|
||||||
|
@mock.patch('novajoin.notifications.NotificationEndpoint'
|
||||||
|
'._generate_hostname')
|
||||||
|
def test_instance_create(self, generate_hostname):
|
||||||
|
event = self._get_event('instance.create.end.json')
|
||||||
|
result = self._run_dispatcher(event)
|
||||||
|
self.assertEqual(result, NotificationResult.HANDLED)
|
||||||
|
|
||||||
|
@mock.patch('novajoin.notifications.NotificationEndpoint'
|
||||||
|
'._generate_hostname')
|
||||||
|
def test_instance_create_wrong_version(self, generate_hostname):
|
||||||
|
event = self._get_event('instance.create.end.json')
|
||||||
|
event['payload']['nova_object.version'] = '999.999'
|
||||||
|
result = self._run_dispatcher(event)
|
||||||
|
self.assertEqual(result, NotificationResult.REQUEUE)
|
||||||
|
|
||||||
|
@mock.patch('novajoin.notifications.glanceclient')
|
||||||
|
@mock.patch('novajoin.notifications.ipaclient')
|
||||||
|
@mock.patch('novajoin.notifications.NotificationEndpoint'
|
||||||
|
'._generate_hostname')
|
||||||
|
def test_instance_update(self, glanceclient, ipaclient, gen_hostname):
|
||||||
|
event = self._get_event('instance.update.json')
|
||||||
|
result = self._run_dispatcher(event)
|
||||||
|
self.assertEqual(result, NotificationResult.HANDLED)
|
||||||
|
|
||||||
|
@mock.patch('novajoin.notifications.glanceclient')
|
||||||
|
@mock.patch('novajoin.notifications.ipaclient')
|
||||||
|
@mock.patch('novajoin.notifications.NotificationEndpoint'
|
||||||
|
'._generate_hostname')
|
||||||
|
def test_instance_delete(self, glanceclient, ipaclient, gen_hostname):
|
||||||
|
event = self._get_event('instance.delete.end.json')
|
||||||
|
result = self._run_dispatcher(event)
|
||||||
|
self.assertEqual(result, NotificationResult.HANDLED)
|
||||||
|
|
||||||
|
@mock.patch('novajoin.notifications.neutronclient')
|
||||||
|
@mock.patch('novajoin.notifications.novaclient')
|
||||||
|
@mock.patch('novajoin.notifications.ipaclient')
|
||||||
|
@mock.patch('novajoin.notifications.NotificationEndpoint'
|
||||||
|
'._generate_hostname')
|
||||||
|
def test_floatingip_associate(self, neutronclient, novaclient,
|
||||||
|
ipaclient, generate_hostname):
|
||||||
|
event = self._get_event('floatingip.update.end_associate.json')
|
||||||
|
result = self._run_dispatcher(event)
|
||||||
|
self.assertEqual(result, NotificationResult.HANDLED)
|
||||||
|
|
||||||
|
@mock.patch('novajoin.notifications.neutronclient')
|
||||||
|
@mock.patch('novajoin.notifications.novaclient')
|
||||||
|
@mock.patch('novajoin.notifications.ipaclient')
|
||||||
|
@mock.patch('novajoin.notifications.NotificationEndpoint'
|
||||||
|
'._generate_hostname')
|
||||||
|
def test_floatingip_disassociate(self, neutronclient, novaclient,
|
||||||
|
ipaclient, generate_hostname):
|
||||||
|
event = self._get_event('floatingip.update.end_disassociate.json')
|
||||||
|
result = self._run_dispatcher(event)
|
||||||
|
self.assertEqual(result, NotificationResult.HANDLED)
|
|
@ -138,6 +138,12 @@ def install(opts):
|
||||||
config.set('keystone_authtoken', 'project_domain_name', 'default')
|
config.set('keystone_authtoken', 'project_domain_name', 'default')
|
||||||
config.set('keystone_authtoken', 'user_domain_id', 'default')
|
config.set('keystone_authtoken', 'user_domain_id', 'default')
|
||||||
|
|
||||||
|
if opts.notification_format == 'versioned':
|
||||||
|
config.set('DEFAULT', 'notification_format', 'versioned')
|
||||||
|
else:
|
||||||
|
config.set('DEFAULT', 'notification_format', 'unversioned')
|
||||||
|
|
||||||
|
config.set('DEFAULT', 'notifications_topic', 'novajoin_notifications')
|
||||||
|
|
||||||
with open(opts.novajoin_conf, 'w') as f:
|
with open(opts.novajoin_conf, 'w') as f:
|
||||||
config.write(f)
|
config.write(f)
|
||||||
|
@ -193,13 +199,21 @@ def install(opts):
|
||||||
'notify_on_state_change',
|
'notify_on_state_change',
|
||||||
'vm_state')
|
'vm_state')
|
||||||
|
|
||||||
config.set('notifications',
|
if opts.notification_format == 'versioned':
|
||||||
'notification_format',
|
config.set('notifications',
|
||||||
'unversioned')
|
'notification_format',
|
||||||
|
'versioned')
|
||||||
|
config.set('notifications',
|
||||||
|
'versioned_notifications_topics',
|
||||||
|
'versioned_notifications,novajoin_notifications')
|
||||||
|
else:
|
||||||
|
config.set('notifications',
|
||||||
|
'notification_format',
|
||||||
|
'unversioned')
|
||||||
|
config.set('oslo_messaging_notifications',
|
||||||
|
'topics',
|
||||||
|
'notifications,novajoin_notifications')
|
||||||
|
|
||||||
config.set('oslo_messaging_notifications',
|
|
||||||
'topics',
|
|
||||||
'notifications,novajoin_notifications')
|
|
||||||
with open(conf, 'w') as f:
|
with open(conf, 'w') as f:
|
||||||
config.write(f)
|
config.write(f)
|
||||||
|
|
||||||
|
@ -259,6 +273,10 @@ def parse_args():
|
||||||
parser.add_argument('--novajoin-conf', dest='novajoin_conf',
|
parser.add_argument('--novajoin-conf', dest='novajoin_conf',
|
||||||
help='novajoin configuration file',
|
help='novajoin configuration file',
|
||||||
default=JOINCONF)
|
default=JOINCONF)
|
||||||
|
parser.add_argument('--notification-format', dest='notification_format',
|
||||||
|
help='The format of notifications to emit and read.',
|
||||||
|
choices=['versioned', 'unversioned'],
|
||||||
|
default='versioned')
|
||||||
parser = configure_ipa.ipa_options(parser)
|
parser = configure_ipa.ipa_options(parser)
|
||||||
|
|
||||||
opts = parser.parse_args()
|
opts = parser.parse_args()
|
||||||
|
|
Loading…
Reference in New Issue