Browse Source

Refactor notifications

Make a registry of different types of processed events
instead of an endless if/elif clause.

Change-Id: I34ebdca82810b9abd46a84aca7f1a8febf718be6
Grzegorz Grasza 4 months ago
parent
commit
7fa5789e51
1 changed files with 150 additions and 117 deletions
  1. 150
    117
      novajoin/notifications.py

+ 150
- 117
novajoin/notifications.py View File

@@ -43,6 +43,10 @@ LOG = logging.getLogger(__name__)
43 43
 BACKOFF = 2
44 44
 
45 45
 
46
+def ipaclient():
47
+    return IPAClient(backoff=BACKOFF)
48
+
49
+
46 50
 def novaclient():
47 51
     session = get_session()
48 52
     return nova_client.Client('2.1', session=session)
@@ -53,6 +57,14 @@ def neutronclient():
53 57
     return neutron_client.Client(session=session)
54 58
 
55 59
 
60
+class Registry(dict):
61
+    def __call__(self, name):
62
+        def decorator(fun):
63
+            self[name] = fun
64
+            return fun
65
+        return decorator
66
+
67
+
56 68
 class NotificationEndpoint(object):
57 69
 
58 70
     filter_rule = oslo_messaging.notify.filter.NotificationFilter(
@@ -63,15 +75,7 @@ class NotificationEndpoint(object):
63 75
                    '^network.floating_ip.(dis)?associate|'
64 76
                    '^floatingip.update.end')
65 77
 
66
-    def _generate_hostname(self, hostname):
67
-        # FIXME: Don't re-calculate the hostname, fetch it from somewhere
68
-        project = 'foo'
69
-        domain = get_domain()
70
-        if CONF.project_subdomain:
71
-            host = '%s.%s.%s' % (hostname, project, domain)
72
-        else:
73
-            host = '%s.%s' % (hostname, domain)
74
-        return host
78
+    event_handlers = Registry()
75 79
 
76 80
     def info(self, ctxt, publisher_id, event_type, payload, metadata):
77 81
         LOG.debug('notification:')
@@ -80,105 +84,124 @@ class NotificationEndpoint(object):
80 84
         LOG.debug("publisher: %s, event: %s, metadata: %s", publisher_id,
81 85
                   event_type, metadata)
82 86
 
83
-        ipaclient = IPAClient(backoff=BACKOFF)
84
-        if event_type == 'compute.instance.create.end':
85
-            hostname = self._generate_hostname(payload.get('hostname'))
86
-            instance_id = payload.get('instance_id')
87
-            LOG.info("Add new host %s (%s)", instance_id, hostname)
88
-        elif event_type == 'compute.instance.update':
89
-            join_controller = join.JoinController(ipaclient)
90
-            hostname_short = payload.get('hostname')
91
-            instance_id = payload.get('instance_id')
92
-            payload_metadata = payload.get('metadata')
93
-            image_metadata = payload.get('image_meta')
94
-
95
-            hostname = self._generate_hostname(hostname_short)
96
-
97
-            enroll = payload_metadata.get('ipa_enroll', '')
98
-            image_enroll = image_metadata.get('ipa_enroll', '')
99
-            if enroll.lower() != 'true' and image_enroll.lower() != 'true':
100
-                LOG.info('IPA enrollment not requested, skipping update of %s',
101
-                         hostname)
102
-                return
103
-            # Ensure this instance exists in nova
104
-            instance = get_instance(instance_id)
105
-            if instance is None:
106
-                msg = 'No such instance-id, %s' % instance_id
107
-                LOG.error(msg)
108
-                return
109
-
110
-            ipaclient.start_batch_operation()
111
-            # key-per-service
112
-            managed_services = [
113
-                payload_metadata[key] for key in payload_metadata.keys()
114
-                if key.startswith('managed_service_')]
115
-            if managed_services:
116
-                join_controller.handle_services(hostname, managed_services)
117
-            # compact json format
118
-            if 'compact_services' in payload_metadata:
119
-                join_controller.handle_compact_services(
120
-                    hostname_short, payload_metadata.get('compact_services'))
121
-            ipaclient.flush_batch_operation()
122
-        elif event_type == 'compute.instance.delete.end':
123
-            hostname_short = payload.get('hostname')
124
-            instance_id = payload.get('instance_id')
125
-            payload_metadata = payload.get('metadata')
126
-            image_metadata = payload.get('image_meta')
127
-
128
-            hostname = self._generate_hostname(hostname_short)
129
-
130
-            enroll = payload_metadata.get('ipa_enroll', '')
131
-            image_enroll = image_metadata.get('ipa_enroll', '')
132
-
133
-            if enroll.lower() != 'true' and image_enroll.lower() != 'true':
134
-                LOG.info('IPA enrollment not requested, skipping delete of %s',
135
-                         hostname)
136
-                return
137
-
138
-            LOG.info("Delete host %s (%s)", instance_id, hostname)
139
-            ipaclient.delete_host(hostname, {})
140
-            self.delete_subhosts(ipaclient, hostname_short, payload_metadata)
141
-        elif event_type == 'network.floating_ip.associate':
142
-            floating_ip = payload.get('floating_ip')
143
-            LOG.info("Associate floating IP %s" % floating_ip)
144
-            nova = novaclient()
145
-            server = nova.servers.get(payload.get('instance_id'))
146
-            if server:
147
-                ipaclient.add_ip(server.get, floating_ip)
148
-            else:
149
-                LOG.error("Could not resolve %s into a hostname",
150
-                          payload.get('instance_id'))
151
-        elif event_type == 'network.floating_ip.disassociate':
152
-            floating_ip = payload.get('floating_ip')
153
-            LOG.info("Disassociate floating IP %s" % floating_ip)
154
-            nova = novaclient()
155
-            server = nova.servers.get(payload.get('instance_id'))
156
-            if server:
157
-                ipaclient.remove_ip(server.name, floating_ip)
158
-            else:
159
-                LOG.error("Could not resolve %s into a hostname",
160
-                          payload.get('instance_id'))
161
-        elif event_type == 'floatingip.update.end':  # Neutron
162
-            floatingip = payload.get('floatingip')
163
-            floating_ip = floatingip.get('floating_ip_address')
164
-            port_id = floatingip.get('port_id')
165
-            LOG.info("Neutron floating IP associate: %s" % floating_ip)
166
-            nova = novaclient()
167
-            neutron = neutronclient()
168
-            search_opts = {'id': port_id}
169
-            ports = neutron.list_ports(**search_opts).get('ports')
170
-            if len(ports) == 1:
171
-                device_id = ports[0].get('device_id')
172
-                if device_id:
173
-                    server = nova.servers.get(device_id)
174
-                    if server:
175
-                        ipaclient.add_ip(server.name, floating_ip)
176
-            else:
177
-                LOG.error("Expected 1 port, got %d", len(ports))
87
+        event_handler = self.event_handlers.get(
88
+            event_type, lambda payload: LOG.error("Status update or unknown"))
89
+        # run event handler for received notification type
90
+        event_handler(self, payload)
91
+
92
+    @event_handlers('compute.instance.create.end')
93
+    def instance_create(self, payload):
94
+        hostname = self._generate_hostname(payload.get('hostname'))
95
+        instance_id = payload.get('instance_id')
96
+        LOG.info("Add new host %s (%s)", instance_id, hostname)
97
+
98
+    @event_handlers('compute.instance.update')
99
+    def instance_update(self, payload):
100
+        ipa = ipaclient()
101
+        join_controller = join.JoinController(ipa)
102
+        hostname_short = payload.get('hostname')
103
+        instance_id = payload.get('instance_id')
104
+        payload_metadata = payload.get('metadata')
105
+        image_metadata = payload.get('image_meta')
106
+
107
+        hostname = self._generate_hostname(hostname_short)
108
+
109
+        enroll = payload_metadata.get('ipa_enroll', '')
110
+        image_enroll = image_metadata.get('ipa_enroll', '')
111
+        if enroll.lower() != 'true' and image_enroll.lower() != 'true':
112
+            LOG.info('IPA enrollment not requested, skipping update of %s',
113
+                     hostname)
114
+            return
115
+        # Ensure this instance exists in nova
116
+        instance = get_instance(instance_id)
117
+        if instance is None:
118
+            msg = 'No such instance-id, %s' % instance_id
119
+            LOG.error(msg)
120
+            return
121
+
122
+        ipa.start_batch_operation()
123
+        # key-per-service
124
+        managed_services = [
125
+            payload_metadata[key] for key in payload_metadata.keys()
126
+            if key.startswith('managed_service_')]
127
+        if managed_services:
128
+            join_controller.handle_services(hostname, managed_services)
129
+        # compact json format
130
+        if 'compact_services' in payload_metadata:
131
+            join_controller.handle_compact_services(
132
+                hostname_short, payload_metadata.get('compact_services'))
133
+        ipa.flush_batch_operation()
134
+
135
+    @event_handlers('compute.instance.delete.end')
136
+    def instance_delete(self, payload):
137
+        hostname_short = payload.get('hostname')
138
+        instance_id = payload.get('instance_id')
139
+        payload_metadata = payload.get('metadata')
140
+        image_metadata = payload.get('image_meta')
141
+
142
+        hostname = self._generate_hostname(hostname_short)
143
+
144
+        enroll = payload_metadata.get('ipa_enroll', '')
145
+        image_enroll = image_metadata.get('ipa_enroll', '')
146
+
147
+        if enroll.lower() != 'true' and image_enroll.lower() != 'true':
148
+            LOG.info('IPA enrollment not requested, skipping delete of %s',
149
+                     hostname)
150
+            return
151
+
152
+        LOG.info("Delete host %s (%s)", instance_id, hostname)
153
+        ipa = ipaclient()
154
+        ipa.delete_host(hostname, {})
155
+        self.delete_subhosts(ipa, hostname_short, payload_metadata)
156
+
157
+    @event_handlers('network.floating_ip.associate')
158
+    def floaitng_ip_associate(self, payload):
159
+        floating_ip = payload.get('floating_ip')
160
+        LOG.info("Associate floating IP %s" % floating_ip)
161
+        ipa = ipaclient()
162
+        nova = novaclient()
163
+        server = nova.servers.get(payload.get('instance_id'))
164
+        if server:
165
+            ipa.add_ip(server.get, floating_ip)
166
+        else:
167
+            LOG.error("Could not resolve %s into a hostname",
168
+                      payload.get('instance_id'))
169
+
170
+    @event_handlers('network.floating_ip.disassociate')
171
+    def floating_ip_disassociate(self, payload):
172
+        floating_ip = payload.get('floating_ip')
173
+        LOG.info("Disassociate floating IP %s" % floating_ip)
174
+        ipa = ipaclient()
175
+        nova = novaclient()
176
+        server = nova.servers.get(payload.get('instance_id'))
177
+        if server:
178
+            ipa.remove_ip(server.name, floating_ip)
178 179
         else:
179
-            LOG.error("Status update or unknown")
180
+            LOG.error("Could not resolve %s into a hostname",
181
+                      payload.get('instance_id'))
182
+
183
+    @event_handlers('floatingip.update.end')
184
+    def floating_ip_update(self, payload):
185
+        """Neutron event"""
186
+        floatingip = payload.get('floatingip')
187
+        floating_ip = floatingip.get('floating_ip_address')
188
+        port_id = floatingip.get('port_id')
189
+        LOG.info("Neutron floating IP associate: %s" % floating_ip)
190
+        ipa = ipaclient()
191
+        nova = novaclient()
192
+        neutron = neutronclient()
193
+        search_opts = {'id': port_id}
194
+        ports = neutron.list_ports(**search_opts).get('ports')
195
+        if len(ports) == 1:
196
+            device_id = ports[0].get('device_id')
197
+            if device_id:
198
+                server = nova.servers.get(device_id)
199
+                if server:
200
+                    ipa.add_ip(server.name, floating_ip)
201
+        else:
202
+            LOG.error("Expected 1 port, got %d", len(ports))
180 203
 
181
-    def delete_subhosts(self, ipaclient, hostname_short, metadata):
204
+    def delete_subhosts(self, ipa, hostname_short, metadata):
182 205
         """Delete subhosts and remove VIPs if possible.
183 206
 
184 207
         Servers can have multiple network interfaces, and therefore can
@@ -199,14 +222,14 @@ class NotificationEndpoint(object):
199 222
             return
200 223
 
201 224
         if 'compact_services' in metadata:
202
-            self.handle_compact_services(ipaclient, hostname_short,
225
+            self.handle_compact_services(ipa, hostname_short,
203 226
                                          metadata.get('compact_services'))
204 227
         managed_services = [metadata[key] for key in metadata.keys()
205 228
                             if key.startswith('managed_service_')]
206 229
         if managed_services:
207
-            self.handle_managed_services(ipaclient, managed_services)
230
+            self.handle_managed_services(ipa, managed_services)
208 231
 
209
-    def handle_compact_services(self, ipaclient, host_short,
232
+    def handle_compact_services(self, ipa, host_short,
210 233
                                 service_repr_json):
211 234
         """Reconstructs and removes subhosts for compact services.
212 235
 
@@ -225,7 +248,7 @@ class NotificationEndpoint(object):
225 248
         service_repr = json.loads(service_repr_json)
226 249
         hosts_found = list()
227 250
 
228
-        ipaclient.start_batch_operation()
251
+        ipa.start_batch_operation()
229 252
         for service_name, net_list in service_repr.items():
230 253
             for network in net_list:
231 254
                 host = "%s.%s" % (host_short, network)
@@ -233,11 +256,11 @@ class NotificationEndpoint(object):
233 256
 
234 257
                 # remove host
235 258
                 if principal_host not in hosts_found:
236
-                    ipaclient.delete_subhost(principal_host)
259
+                    ipa.delete_subhost(principal_host)
237 260
                     hosts_found.append(principal_host)
238
-        ipaclient.flush_batch_operation()
261
+        ipa.flush_batch_operation()
239 262
 
240
-    def handle_managed_services(self, ipaclient, services):
263
+    def handle_managed_services(self, ipa, services):
241 264
         """Delete any managed services if possible.
242 265
 
243 266
            Checks to see if the managed service subhost has no managed hosts
@@ -250,19 +273,29 @@ class NotificationEndpoint(object):
250 273
         for principal in services:
251 274
             if principal not in services_deleted:
252 275
                 try:
253
-                    if ipaclient.service_has_hosts(principal):
276
+                    if ipa.service_has_hosts(principal):
254 277
                         continue
255 278
                 except KeyError:
256 279
                     continue
257
-                ipaclient.delete_service(principal, batch=False)
280
+                ipa.delete_service(principal, batch=False)
258 281
                 services_deleted.append(principal)
259 282
 
260 283
             principal_host = principal.split('/', 1)[1]
261 284
             if principal_host not in hosts_deleted:
262
-                if not ipaclient.host_has_services(principal_host):
263
-                    ipaclient.delete_subhost(principal_host, batch=False)
285
+                if not ipa.host_has_services(principal_host):
286
+                    ipa.delete_subhost(principal_host, batch=False)
264 287
                     hosts_deleted.append(principal_host)
265 288
 
289
+    def _generate_hostname(self, hostname):
290
+        # FIXME: Don't re-calculate the hostname, fetch it from somewhere
291
+        project = 'foo'
292
+        domain = get_domain()
293
+        if CONF.project_subdomain:
294
+            host = '%s.%s.%s' % (hostname, project, domain)
295
+        else:
296
+            host = '%s.%s' % (hostname, domain)
297
+        return host
298
+
266 299
 
267 300
 def main():
268 301
     register_keystoneauth_opts(CONF)

Loading…
Cancel
Save