Merge from OSM SO master
[osm/SO.git] / rwlaunchpad / plugins / rwmonitor / rift / tasklets / rwmonitor / core.py
1
2 #
3 # Copyright 2016 RIFT.IO Inc
4 #
5 # Licensed under the Apache License, Version 2.0 (the "License");
6 # you may not use this file except in compliance with the License.
7 # You may obtain a copy of the License at
8 #
9 # http://www.apache.org/licenses/LICENSE-2.0
10 #
11 # Unless required by applicable law or agreed to in writing, software
12 # distributed under the License is distributed on an "AS IS" BASIS,
13 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 # See the License for the specific language governing permissions and
15 # limitations under the License.
16 #
17
18
19 import abc
20 import asyncio
21 import collections
22 import concurrent.futures
23 import importlib
24 import time
25
26 import gi
27 gi.require_version('RwVnfrYang', '1.0')
28 gi.require_version('RwMon', '1.0')
29 from gi.repository import (
30 RwTypes,
31 RwVnfrYang,
32 )
33
34 import rw_peas
35
36
37 class VdurMissingVimIdError(Exception):
38 def __init__(self, vdur_id):
39 super().__init__("VDUR:{} is has no VIM ID".format(vdur_id))
40
41
42 class VdurAlreadyRegisteredError(Exception):
43 def __init__(self, vdur_id):
44 super().__init__("VDUR:{} is already registered".format(vdur_id))
45
46
47 class AccountInUseError(Exception):
48 pass
49
50
51 class UnknownAccountError(Exception):
52 pass
53
54
55 class AccountAlreadyRegisteredError(Exception):
56 def __init__(self, account_name):
57 msg = "'{}' already registered".format(account_name)
58 super().__init__(account_name)
59
60
61 class PluginUnavailableError(Exception):
62 pass
63
64
65 class PluginNotSupportedError(PluginUnavailableError):
66 pass
67
68
69 class AlarmCreateError(Exception):
70 def __init__(self):
71 super().__init__("failed to create alarm")
72
73
74 class AlarmDestroyError(Exception):
75 def __init__(self):
76 super().__init__("failed to destroy alarm")
77
78
79 class PluginFactory(object):
80 __metaclass__ = abc.ABCMeta
81
82 @abc.abstractmethod
83 def create(self, cloud_account, plugin_name):
84 pass
85
86 @property
87 def name(self):
88 return self.__class__.PLUGIN_NAME
89
90 @property
91 def fallbacks(self):
92 try:
93 return list(self.__class__.FALLBACKS)
94 except Exception:
95 return list()
96
97
98 class MonascaPluginFactory(PluginFactory):
99 PLUGIN_NAME = "monasca"
100 FALLBACKS = ["ceilometer",]
101
102 def create(self, cloud_account):
103 raise PluginUnavailableError()
104
105
106 class CeilometerPluginFactory(PluginFactory):
107 PLUGIN_NAME = "ceilometer"
108 FALLBACKS = ["unavailable",]
109
110 def create(self, cloud_account):
111 plugin = rw_peas.PeasPlugin("rwmon_ceilometer", 'RwMon-1.0')
112 impl = plugin.get_interface("Monitoring")
113
114 # Check that the plugin is available on the platform associated with
115 # the cloud account
116 _, available = impl.nfvi_metrics_available(cloud_account)
117 if not available:
118 raise PluginUnavailableError()
119
120 return impl
121
122
123 class UnavailablePluginFactory(PluginFactory):
124 PLUGIN_NAME = "unavailable"
125
126 class UnavailablePlugin(object):
127 def nfvi_metrics_available(self, cloud_account):
128 return None, False
129
130 def create(self, cloud_account):
131 return UnavailablePluginFactory.UnavailablePlugin()
132
133
134 class MockPluginFactory(PluginFactory):
135 PLUGIN_NAME = "mock"
136 FALLBACKS = ["unavailable",]
137
138 def create(self, cloud_account):
139 plugin = rw_peas.PeasPlugin("rwmon_mock", 'RwMon-1.0')
140 impl = plugin.get_interface("Monitoring")
141
142 # Check that the plugin is available on the platform associated with
143 # the cloud account
144 _, available = impl.nfvi_metrics_available(cloud_account)
145 if not available:
146 raise PluginUnavailableError()
147
148 return impl
149
150
151 class NfviMetricsPluginManager(object):
152 def __init__(self, log):
153 self._plugins = dict()
154 self._log = log
155 self._factories = dict()
156
157 self.register_plugin_factory(MockPluginFactory())
158 self.register_plugin_factory(CeilometerPluginFactory())
159 self.register_plugin_factory(MonascaPluginFactory())
160 self.register_plugin_factory(UnavailablePluginFactory())
161
162 @property
163 def log(self):
164 return self._log
165
166 def register_plugin_factory(self, factory):
167 self._factories[factory.name] = factory
168
169 def plugin(self, account_name):
170 return self._plugins[account_name]
171
172 def register(self, cloud_account, plugin_name):
173 # Check to see if the cloud account has already been registered
174 if cloud_account.name in self._plugins:
175 raise AccountAlreadyRegisteredError(cloud_account.name)
176
177 if plugin_name not in self._factories:
178 raise PluginNotSupportedError(plugin_name)
179
180 # Create a plugin from one of the factories
181 fallbacks = [plugin_name,]
182
183 while fallbacks:
184 name = fallbacks.pop(0)
185 try:
186 factory = self._factories[name]
187 plugin = factory.create(cloud_account)
188 self._plugins[cloud_account.name] = plugin
189 return
190
191 except PluginUnavailableError as e:
192 self.log.warning("plugin for {} unavailable".format(name))
193 fallbacks.extend(factory.fallbacks)
194
195 raise PluginUnavailableError()
196
197 def unregister(self, account_name):
198 if account_name in self._plugins:
199 del self._plugins[account_name]
200
201
202 class NfviMetrics(object):
203 """
204 The NfviMetrics class contains the logic to retrieve NFVI metrics for a
205 particular VDUR. Of particular importance is that this object caches the
206 metrics until the data become stale so that it does not create excessive
207 load upon the underlying data-source.
208 """
209
210 # The sample interval defines the maximum time (secs) that metrics will be
211 # cached for. This duration should coincide with the sampling interval used
212 # by the underlying data-source to capture metrics.
213 SAMPLE_INTERVAL = 10
214
215 # The maximum time (secs) an instance will wait for a request to the data
216 # source to be completed
217 TIMEOUT = 2
218
219 def __init__(self, log, loop, account, plugin, vdur):
220 """Creates an instance of NfviMetrics
221
222 Arguments:
223 manager - a NfviInterface instance
224 account - a CloudAccount instance
225 plugin - an NFVI plugin
226 vdur - a VDUR instance
227
228 """
229 self._log = log
230 self._loop = loop
231 self._account = account
232 self._plugin = plugin
233 self._timestamp = 0
234 self._metrics = RwVnfrYang.YangData_RwProject_Project_VnfrCatalog_Vnfr_Vdur_NfviMetrics()
235 self._vdur = vdur
236 self._vim_id = vdur.vim_id
237 self._updating = None
238
239 @property
240 def log(self):
241 """The logger used by NfviMetrics"""
242 return self._log
243
244 @property
245 def loop(self):
246 """The current asyncio loop"""
247 return self._loop
248
249 @property
250 def vdur(self):
251 """The VDUR that these metrics are associated with"""
252 return self._vdur
253
254 def retrieve(self):
255 """Return the NFVI metrics for this VDUR
256
257 This function will immediately return the current, known NFVI metrics
258 for the associated VDUR. It will also, if the data are stale, schedule
259 a call to the data-source to retrieve new data.
260
261 """
262 if self.should_update():
263 self._updating = self.loop.create_task(self.update())
264
265 return self._metrics
266
267 def should_update(self):
268 """Return a boolean indicating whether an update should be performed"""
269 running = self._updating is not None and not self._updating.done()
270 overdue = time.time() > self._timestamp + NfviMetrics.SAMPLE_INTERVAL
271
272 return overdue and not running
273
274 @asyncio.coroutine
275 def update(self):
276 """Update the NFVI metrics for the associated VDUR
277
278 This coroutine will request new metrics from the data-source and update
279 the current metrics.
280
281 """
282 try:
283 try:
284 # Make the request to the plugin in a separate thread and do
285 # not exceed the timeout
286 _, metrics = yield from asyncio.wait_for(
287 self.loop.run_in_executor(
288 None,
289 self._plugin.nfvi_metrics,
290 self._account,
291 self._vim_id,
292 ),
293 timeout=NfviMetrics.TIMEOUT,
294 loop=self.loop,
295 )
296
297 except asyncio.TimeoutError:
298 msg = "timeout on request for nfvi metrics (vim-id = {})"
299 self.log.warning(msg.format(self._vim_id))
300 return
301
302 except Exception as e:
303 self.log.exception(e)
304 return
305
306 try:
307 # Create uninitialized metric structure
308 vdu_metrics = RwVnfrYang.YangData_RwProject_Project_VnfrCatalog_Vnfr_Vdur_NfviMetrics()
309
310 # VCPU
311 vdu_metrics.vcpu.total = self.vdur.vm_flavor.vcpu_count
312 vdu_metrics.vcpu.utilization = metrics.vcpu.utilization
313
314 # Memory (in bytes)
315 vdu_metrics.memory.used = metrics.memory.used
316 vdu_metrics.memory.total = self.vdur.vm_flavor.memory_mb
317 vdu_metrics.memory.utilization = 100 * vdu_metrics.memory.used / vdu_metrics.memory.total
318
319 # Storage
320 try:
321 vdu_metrics.storage.used = metrics.storage.used
322 if self.vdur.has_field('volumes'):
323 for volume in self.vdur.volumes:
324 if vdu_metrics.storage.total is None:
325 vdu_metrics.storage.total = 1e9 * volume.size
326 else:
327 vdu_metrics.storage.total += (1e9 * volume.size)
328 else:
329 vdu_metrics.storage.total = 1e9 * self.vdur.vm_flavor.storage_gb
330 utilization = 100 * vdu_metrics.storage.used / vdu_metrics.storage.total
331 if utilization > 100:
332 utilization = 100
333 vdu_metrics.storage.utilization = utilization
334 except ZeroDivisionError:
335 vdu_metrics.storage.utilization = 0
336
337 # Network (incoming)
338 vdu_metrics.network.incoming.packets = metrics.network.incoming.packets
339 vdu_metrics.network.incoming.packet_rate = metrics.network.incoming.packet_rate
340 vdu_metrics.network.incoming.bytes = metrics.network.incoming.bytes
341 vdu_metrics.network.incoming.byte_rate = metrics.network.incoming.byte_rate
342
343 # Network (outgoing)
344 vdu_metrics.network.outgoing.packets = metrics.network.outgoing.packets
345 vdu_metrics.network.outgoing.packet_rate = metrics.network.outgoing.packet_rate
346 vdu_metrics.network.outgoing.bytes = metrics.network.outgoing.bytes
347 vdu_metrics.network.outgoing.byte_rate = metrics.network.outgoing.byte_rate
348
349 # External ports
350 vdu_metrics.external_ports.total = len(self.vdur.external_interface)
351
352 # Internal ports
353 vdu_metrics.internal_ports.total = len(self.vdur.internal_interface)
354
355 self._metrics = vdu_metrics
356
357 except Exception as e:
358 self.log.exception(e)
359
360 finally:
361 # Regardless of the result of the query, we want to make sure that
362 # we do not poll the data source until another sample duration has
363 # passed.
364 self._timestamp = time.time()
365
366
367 class NfviMetricsCache(object):
368 def __init__(self, log, loop, plugin_manager):
369 self._log = log
370 self._loop = loop
371 self._plugin_manager = plugin_manager
372 self._nfvi_metrics = dict()
373
374 self._vim_to_vdur = dict()
375 self._vdur_to_vim = dict()
376
377 def create_entry(self, account, vdur):
378 plugin = self._plugin_manager.plugin(account.name)
379 metrics = NfviMetrics(self._log, self._loop, account, plugin, vdur)
380 self._nfvi_metrics[vdur.vim_id] = metrics
381
382 self._vim_to_vdur[vdur.vim_id] = vdur.id
383 self._vdur_to_vim[vdur.id] = vdur.vim_id
384
385 def destroy_entry(self, vdur_id):
386 vim_id = self._vdur_to_vim[vdur_id]
387
388 del self._nfvi_metrics[vim_id]
389 del self._vdur_to_vim[vdur_id]
390 del self._vim_to_vdur[vim_id]
391
392 def retrieve(self, vim_id):
393 return self._nfvi_metrics[vim_id].retrieve()
394
395 def to_vim_id(self, vdur_id):
396 return self._vdur_to_vim[vdur_id]
397
398 def to_vdur_id(self, vim_id):
399 return self._vim_to_vdur[vim_id]
400
401 def contains_vdur_id(self, vdur_id):
402 return vdur_id in self._vdur_to_vim
403
404 def contains_vim_id(self, vim_id):
405 return vim_id in self._vim_to_vdur
406
407
408 class NfviInterface(object):
409 """
410 The NfviInterface serves as an interface for communicating with the
411 underlying infrastructure, i.e. retrieving metrics for VDURs that have been
412 registered with it and managing alarms.
413
414 The NfviInterface should only need to be invoked using a cloud account and
415 optionally a VIM ID; It should not need to handle mapping from VDUR ID to
416 VIM ID.
417 """
418
419 def __init__(self, loop, log, plugin_manager, cache):
420 """Creates an NfviInterface instance
421
422 Arguments:
423 loop - an event loop
424 log - a logger
425 plugin_manager - an instance of NfviMetricsPluginManager
426 cache - an instance of NfviMetricsCache
427
428 """
429 self._executor = concurrent.futures.ThreadPoolExecutor(max_workers=16)
430 self._plugin_manager = plugin_manager
431 self._cache = cache
432 self._loop = loop
433 self._log = log
434
435 @property
436 def loop(self):
437 """The event loop used by this NfviInterface"""
438 return self._loop
439
440 @property
441 def log(self):
442 """The event log used by this NfviInterface"""
443 return self._log
444
445 @property
446 def metrics(self):
447 """The list of metrics contained in this NfviInterface"""
448 return list(self._cache._nfvi_metrics.values())
449
450 def nfvi_metrics_available(self, account):
451 plugin = self._plugin_manager.plugin(account.name)
452 _, available = plugin.nfvi_metrics_available(account)
453 return available
454
455 def retrieve(self, vdur_id):
456 """Returns the NFVI metrics for the specified VDUR
457
458 Note, a VDUR must be registered with a NfviInterface before
459 metrics can be retrieved for it.
460
461 Arguments:
462 vdur_id - the ID of the VDUR to whose metrics should be retrieve
463
464 Returns:
465 An NfviMetrics object for the specified VDUR
466
467 """
468 return self._cache.retrieve(self._cache.to_vim_id(vdur_id))
469
470 @asyncio.coroutine
471 def alarm_create(self, account, vim_id, alarm, timeout=5):
472 """Create a new alarm
473
474 Arguments:
475 account - a CloudAccount instance
476 vim_id - the VM to associate with this alarm
477 alarm - an alarm structure
478 timeout - the request timeout (sec)
479
480 Raises:
481 If the data source does not respond in a timely manner, an
482 asyncio.TimeoutError will be raised.
483
484 """
485 plugin = self._plugin_manager.plugin(account.name)
486 status = yield from asyncio.wait_for(
487 self.loop.run_in_executor(
488 None,
489 plugin.do_alarm_create,
490 account,
491 vim_id,
492 alarm,
493 ),
494 timeout=timeout,
495 loop=self.loop,
496 )
497
498 if status == RwTypes.RwStatus.FAILURE:
499 raise AlarmCreateError()
500
501 @asyncio.coroutine
502 def alarm_destroy(self, account, alarm_id, timeout=5):
503 """Destroy an existing alarm
504
505 Arguments:
506 account - a CloudAccount instance
507 alarm_id - the identifier of the alarm to destroy
508 timeout - the request timeout (sec)
509
510 Raises:
511 If the data source does not respond in a timely manner, an
512 asyncio.TimeoutError will be raised.
513
514 """
515 plugin = self._plugin_manager.plugin(account.name)
516 status = yield from asyncio.wait_for(
517 self.loop.run_in_executor(
518 None,
519 plugin.do_alarm_delete,
520 account,
521 alarm_id,
522 ),
523 timeout=timeout,
524 loop=self.loop,
525 )
526
527 if status == RwTypes.RwStatus.FAILURE:
528 raise AlarmDestroyError()
529
530
531 class InstanceConfiguration(object):
532 """
533 The InstanceConfiguration class represents configuration information that
534 affects the behavior of the monitor. Essentially this class should contain
535 not functional behavior but serve as a convenient way to share data amongst
536 the components of the monitoring system.
537 """
538
539 def __init__(self):
540 self.polling_period = None
541 self.max_polling_frequency = None
542 self.min_cache_lifetime = None
543 self.public_ip = None
544
545
546 class Monitor(object):
547 """
548 The Monitor class is intended to act as a unifying interface for the
549 different sub-systems that are used to monitor the NFVI.
550 """
551
552 def __init__(self, loop, log, config, project):
553 """Create a Monitor object
554
555 Arguments:
556 loop - an event loop
557 log - the logger used by this object
558 config - an instance of InstanceConfiguration
559 project - an instance of the project
560
561 """
562 self._loop = loop
563 self._log = log
564 self._project = project
565
566 self._cloud_accounts = dict()
567 self._nfvi_plugins = NfviMetricsPluginManager(log)
568 self._cache = NfviMetricsCache(log, loop, self._nfvi_plugins)
569 self._nfvi_interface = NfviInterface(loop, log, self._nfvi_plugins, self._cache)
570 self._config = config
571 self._vnfrs = dict()
572 self._vnfr_to_vdurs = collections.defaultdict(set)
573 self._alarms = collections.defaultdict(list)
574
575 @property
576 def loop(self):
577 """The event loop used by this object"""
578 return self._loop
579
580 @property
581 def log(self):
582 """The event log used by this object"""
583 return self._log
584
585 @property
586 def project(self):
587 return self._project
588
589 @property
590 def cache(self):
591 """The NFVI metrics cache"""
592 return self._cache
593
594 @property
595 def metrics(self):
596 """The list of metrics contained in this Monitor"""
597 return self._nfvi_interface.metrics
598
599 def nfvi_metrics_available(self, account):
600 """Returns a boolean indicating whether NFVI metrics are available
601
602 Arguments:
603 account - the name of the cloud account to check
604
605 Returns:
606 a boolean indicating availability of NFVI metrics
607
608 """
609 if account not in self._cloud_accounts:
610 return False
611
612 cloud_account = self._cloud_accounts[account]
613 return self._nfvi_interface.nfvi_metrics_available(cloud_account)
614
615 def add_cloud_account(self, account):
616 """Add a cloud account to the monitor
617
618 Arguments:
619 account - a cloud account object
620
621 Raises:
622 If the cloud account has already been added to the monitor, an
623 AccountAlreadyRegisteredError is raised.
624
625 """
626 if account.name in self._cloud_accounts:
627 raise AccountAlreadyRegisteredError(account.name)
628
629 self._cloud_accounts[account.name] = account
630
631 if account.account_type == "openstack":
632 self.register_cloud_account(account, "monasca")
633 else:
634 self.register_cloud_account(account, "mock")
635
636 def remove_cloud_account(self, account_name):
637 """Remove a cloud account from the monitor
638
639 Arguments:
640 account_name - removes the cloud account that has this name
641
642 Raises:
643 If the specified cloud account cannot be found, an
644 UnknownAccountError is raised.
645
646 """
647 if account_name not in self._cloud_accounts:
648 raise UnknownAccountError()
649
650 # Make sure that there are no VNFRs associated with this account
651 for vnfr in self._vnfrs.values():
652 if vnfr.cloud_account == account_name:
653 raise AccountInUseError()
654
655 del self._cloud_accounts[account_name]
656 self._nfvi_plugins.unregister(account_name)
657
658 def get_cloud_account(self, account_name):
659 """Returns a cloud account by name
660
661 Arguments:
662 account_name - the name of the account to return
663
664 Raises:
665 An UnknownAccountError is raised if there is not account object
666 associated with the provided name
667
668 Returns:
669 A cloud account object
670
671 """
672 if account_name not in self._cloud_accounts:
673 raise UnknownAccountError()
674
675 return self._cloud_accounts[account_name]
676
677 def register_cloud_account(self, account, plugin_name):
678 """Register a cloud account with an NFVI plugin
679
680 Note that a cloud account can only be registered for one plugin at a
681 time.
682
683 Arguments:
684 account - the cloud account to associate with the plugin
685 plugin_name - the name of the plugin to use
686
687 """
688 self._nfvi_plugins.register(account, plugin_name)
689
690 def add_vnfr(self, vnfr):
691 """Add a VNFR to the monitor
692
693 Arguments:
694 vnfr - a VNFR object
695
696 Raises:
697 An UnknownAccountError is raised if the account name contained in
698 the VNFR does not reference a cloud account that has been added to
699 the monitor.
700
701 """
702 if vnfr.cloud_account not in self._cloud_accounts:
703 raise UnknownAccountError()
704
705 account = self._cloud_accounts[vnfr.cloud_account]
706
707 for vdur in vnfr.vdur:
708 try:
709 self.add_vdur(account, vdur)
710 self._vnfr_to_vdurs[vnfr.id].add(vdur.id)
711 except (VdurMissingVimIdError, VdurAlreadyRegisteredError):
712 pass
713
714 self._vnfrs[vnfr.id] = vnfr
715
716 def update_vnfr(self, vnfr):
717 """Updates the VNFR information in the monitor
718
719 Arguments:
720 vnfr - a VNFR object
721
722 Raises:
723 An UnknownAccountError is raised if the account name contained in
724 the VNFR does not reference a cloud account that has been added to
725 the monitor.
726
727 """
728 if vnfr.cloud_account not in self._cloud_accounts:
729 raise UnknownAccountError()
730
731 account = self._cloud_accounts[vnfr.cloud_account]
732
733 for vdur in vnfr.vdur:
734 try:
735 self.add_vdur(account, vdur)
736 self._vnfr_to_vdurs[vnfr.id].add(vdur.id)
737 except (VdurMissingVimIdError, VdurAlreadyRegisteredError):
738 pass
739
740 def remove_vnfr(self, vnfr_id):
741 """Remove a VNFR from the monitor
742
743 Arguments:
744 vnfr_id - the ID of the VNFR to remove
745
746 """
747 vdur_ids = self._vnfr_to_vdurs[vnfr_id]
748
749 for vdur_id in vdur_ids:
750 self.remove_vdur(vdur_id)
751
752 del self._vnfrs[vnfr_id]
753 del self._vnfr_to_vdurs[vnfr_id]
754
755 def add_vdur(self, account, vdur):
756 """Adds a VDUR to the monitor
757
758 Adding a VDUR to the monitor will automatically create a NFVI metrics
759 object that is associated with the VDUR so that the monitor cane
760 provide the NFVI metrics associated with the VDUR.
761
762 Arguments:
763 account - the cloud account associated with the VNFR that contains
764 the provided VDUR
765 vdur - a VDUR object
766
767 Raises:
768 A VdurMissingVimIdError is raised if the provided VDUR does not
769 contain a VIM ID. A VdurAlreadyRegisteredError is raised if the ID
770 associated with the VDUR has already been registered.
771
772 """
773 if not vdur.vim_id:
774 raise VdurMissingVimIdError(vdur.id)
775
776 if self.is_registered_vdur(vdur.id):
777 raise VdurAlreadyRegisteredError(vdur.id)
778
779 self.cache.create_entry(account, vdur)
780
781 def remove_vdur(self, vdur_id):
782 """Removes a VDUR from the monitor
783
784 Arguments:
785 vdur_id - the ID of the VDUR to remove
786
787 """
788 self.cache.destroy_entry(vdur_id)
789
790 # Schedule any alarms associated with the VDUR for destruction
791 for account_name, alarm_id in self._alarms[vdur_id]:
792 self.loop.create_task(self.destroy_alarm(account_name, alarm_id))
793
794 del self._alarms[vdur_id]
795
796 def list_vdur(self, vnfr_id):
797 """Returns a list of VDURs
798
799 Arguments:
800 vnfr_id - the identifier of the VNFR contains the VDURs
801
802 Returns:
803 A list of VDURs
804
805 """
806 return self._vnfrs[vnfr_id].vdur
807
808 def is_registered_vnfr(self, vnfr_id):
809 """Returns True if the VNFR is registered with the monitor
810
811 Arguments:
812 vnfr_id - the ID of the VNFR to check
813
814 Returns:
815 True if the VNFR is registered and False otherwise.
816
817 """
818 return vnfr_id in self._vnfrs
819
820 def is_registered_vdur(self, vdur_id):
821 """Returns True if the VDUR is registered with the monitor
822
823 Arguments:
824 vnfr_id - the ID of the VDUR to check
825
826 Returns:
827 True if the VDUR is registered and False otherwise.
828
829 """
830 return self.cache.contains_vdur_id(vdur_id)
831
832 def retrieve_nfvi_metrics(self, vdur_id):
833 """Retrieves the NFVI metrics associated with a VDUR
834
835 Arguments:
836 vdur_id - the ID of the VDUR whose metrics are to be retrieved
837
838 Returns:
839 NFVI metrics for a VDUR
840
841 """
842 return self._nfvi_interface.retrieve(vdur_id)
843
844 @asyncio.coroutine
845 def create_alarm(self, account_name, vdur_id, alarm):
846 """Create a new alarm
847
848 This function create an alarm and augments the provided endpoints with
849 endpoints to the launchpad if the launchpad has a public IP. The added
850 endpoints are of the form,
851
852 http://{host}:4568/{platform}/{vdur_id}/{action}
853
854 where the 'action' is one of 'ok', 'alarm', or 'insufficient_data'. The
855 messages that are pushed to the launchpad are not defined by RIFT so
856 we need to know which platform an alarm is sent from in order to
857 properly parse it.
858
859
860 Arguments:
861 account_name - the name of the account to use to create the alarm
862 vdur_id - the identifier of the VDUR to associated with the
863 alarm. If the identifier is None, the alarm is not
864 associated with a specific VDUR.
865 alarm - the alarm data
866
867 """
868 account = self.get_cloud_account(account_name)
869 vim_id = self.cache.to_vim_id(vdur_id)
870
871 # If the launchpad has a public IP, augment the action webhooks to
872 # include the launchpad so that the alarms can be broadcast as event
873 # notifications.
874 if self._config.public_ip is not None:
875 url = "http://{host}:4568/{platform}/{vdur_id}".format(
876 host=self._config.public_ip,
877 platform=account.account_type,
878 vdur_id=vudr_id,
879 )
880 alarm.actions.ok.add().url = url + "/ok"
881 alarm.actions.alarm.add().url = url + "/alarm"
882 alarm.actions.alarm.add().url = url + "/insufficient_data"
883
884 yield from self._nfvi_interface.alarm_create(account, vim_id, alarm)
885
886 # Associate the VDUR ID with the alarm ID
887 self._alarms[vdur_id].append((account_name, alarm.alarm_id))
888
889 @asyncio.coroutine
890 def destroy_alarm(self, account_name, alarm_id):
891 """Destroy an existing alarm
892
893 Arugments:
894 account_name - the name of the account that owns the alert
895 alarm_id - the identifier of the alarm to destroy
896
897 """
898 account = self.get_cloud_account(account_name)
899 yield from self._nfvi_interface.alarm_destroy(account, alarm_id)