3 # Copyright 2016 RIFT.IO Inc
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
9 # http://www.apache.org/licenses/LICENSE-2.0
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.
22 import concurrent
.futures
27 gi
.require_version('RwVnfrYang', '1.0')
28 gi
.require_version('RwMon', '1.0')
29 from gi
.repository
import (
37 class VdurMissingVimIdError(Exception):
38 def __init__(self
, vdur_id
):
39 super().__init
__("VDUR:{} is has no VIM ID".format(vdur_id
))
42 class VdurAlreadyRegisteredError(Exception):
43 def __init__(self
, vdur_id
):
44 super().__init
__("VDUR:{} is already registered".format(vdur_id
))
47 class AccountInUseError(Exception):
51 class UnknownAccountError(Exception):
55 class AccountAlreadyRegisteredError(Exception):
56 def __init__(self
, account_name
):
57 msg
= "'{}' already registered".format(account_name
)
58 super().__init
__(account_name
)
61 class PluginUnavailableError(Exception):
65 class PluginNotSupportedError(PluginUnavailableError
):
69 class AlarmCreateError(Exception):
71 super().__init
__("failed to create alarm")
74 class AlarmDestroyError(Exception):
76 super().__init
__("failed to destroy alarm")
79 class PluginFactory(object):
80 __metaclass__
= abc
.ABCMeta
83 def create(self
, cloud_account
, plugin_name
):
88 return self
.__class
__.PLUGIN_NAME
93 return list(self
.__class
__.FALLBACKS
)
97 class MonascaPluginFactory(PluginFactory
):
98 PLUGIN_NAME
= "monasca"
99 FALLBACKS
= ["ceilometer",]
101 def create(self
, cloud_account
):
102 raise PluginUnavailableError()
105 class CeilometerPluginFactory(PluginFactory
):
106 PLUGIN_NAME
= "ceilometer"
107 FALLBACKS
= ["unavailable",]
109 def create(self
, cloud_account
):
110 plugin
= rw_peas
.PeasPlugin("rwmon_ceilometer", 'RwMon-1.0')
111 impl
= plugin
.get_interface("Monitoring")
113 # Check that the plugin is available on the platform associated with
115 _
, available
= impl
.nfvi_metrics_available(cloud_account
)
117 raise PluginUnavailableError()
121 class BrocadeVcpePluginFactory(PluginFactory
):
122 PLUGIN_NAME
= "brocade_vcpe"
123 FALLBACKS
= ["unavailable",]
125 def create(self
, cloud_account
):
126 plugin
= rw_peas
.PeasPlugin("rwmon_brocade", 'RwMon-1.0')
127 impl
= plugin
.get_interface("Monitoring")
129 # Check that the plugin is available on the platform associated with
131 _
, available
= impl
.nfvi_metrics_available(cloud_account
)
133 raise PluginUnavailableError()
137 class UnavailablePluginFactory(PluginFactory
):
138 PLUGIN_NAME
= "unavailable"
140 class UnavailablePlugin(object):
141 def nfvi_metrics_available(self
, cloud_account
):
144 def create(self
, cloud_account
):
145 return UnavailablePluginFactory
.UnavailablePlugin()
148 class MockPluginFactory(PluginFactory
):
150 FALLBACKS
= ["unavailable",]
152 def create(self
, cloud_account
):
153 plugin
= rw_peas
.PeasPlugin("rwmon_mock", 'RwMon-1.0')
154 impl
= plugin
.get_interface("Monitoring")
156 # Check that the plugin is available on the platform associated with
158 _
, available
= impl
.nfvi_metrics_available(cloud_account
)
160 raise PluginUnavailableError()
165 class NfviMetricsPluginManager(object):
166 def __init__(self
, log
):
167 self
._plugins
= dict()
169 self
._factories
= dict()
171 self
.register_plugin_factory(MockPluginFactory())
172 self
.register_plugin_factory(CeilometerPluginFactory())
173 self
.register_plugin_factory(MonascaPluginFactory())
174 self
.register_plugin_factory(UnavailablePluginFactory())
175 self
.register_plugin_factory(BrocadeVcpePluginFactory())
182 def register_plugin_factory(self
, factory
):
183 self
._factories
[factory
.name
] = factory
185 def plugin(self
, account_name
):
186 return self
._plugins
[account_name
]
188 def register(self
, cloud_account
, plugin_name
):
189 # Check to see if the cloud account has already been registered
190 if cloud_account
.name
in self
._plugins
:
191 raise AccountAlreadyRegisteredError(cloud_account
.name
)
193 if plugin_name
not in self
._factories
:
194 raise PluginNotSupportedError(plugin_name
)
196 # Create a plugin from one of the factories
197 fallbacks
= [plugin_name
,]
200 name
= fallbacks
.pop(0)
202 factory
= self
._factories
[name
]
203 plugin
= factory
.create(cloud_account
)
205 self
._plugins
[cloud_account
.name
] = plugin
208 except PluginUnavailableError
as e
:
209 self
.log
.warning("plugin for {} unavailable".format(name
))
210 fallbacks
.extend(factory
.fallbacks
)
212 raise PluginUnavailableError()
214 def unregister(self
, account_name
):
215 if account_name
in self
._plugins
:
216 del self
._plugins
[account_name
]
219 class NfviMetrics(object):
221 The NfviMetrics class contains the logic to retrieve NFVI metrics for a
222 particular VDUR. Of particular importance is that this object caches the
223 metrics until the data become stale so that it does not create excessive
224 load upon the underlying data-source.
227 # The sample interval defines the maximum time (secs) that metrics will be
228 # cached for. This duration should coincide with the sampling interval used
229 # by the underlying data-source to capture metrics.
232 # The maximum time (secs) an instance will wait for a request to the data
233 # source to be completed
236 def __init__(self
, log
, loop
, account
, plugin
, vdur
):
237 """Creates an instance of NfviMetrics
240 manager - a NfviInterface instance
241 account - a CloudAccount instance
242 plugin - an NFVI plugin
243 vdur - a VDUR instance
248 self
._account
= account
249 self
._plugin
= plugin
251 self
._metrics
= RwVnfrYang
.YangData_RwProject_Project_VnfrCatalog_Vnfr_Vdur_NfviMetrics()
253 self
._vim
_id
= vdur
.vim_id
254 self
._updating
= None
258 """The logger used by NfviMetrics"""
263 """The current asyncio loop"""
268 """The VDUR that these metrics are associated with"""
272 """Return the NFVI metrics for this VDUR
274 This function will immediately return the current, known NFVI metrics
275 for the associated VDUR. It will also, if the data are stale, schedule
276 a call to the data-source to retrieve new data.
279 if self
.should_update():
280 self
._updating
= self
.loop
.create_task(self
.update())
284 def should_update(self
):
285 """Return a boolean indicating whether an update should be performed"""
286 running
= self
._updating
is not None and not self
._updating
.done()
287 overdue
= time
.time() > self
._timestamp
+ NfviMetrics
.SAMPLE_INTERVAL
289 return overdue
and not running
293 """Update the NFVI metrics for the associated VDUR
295 This coroutine will request new metrics from the data-source and update
301 # Make the request to the plugin in a separate thread and do
302 # not exceed the timeout
303 _
, metrics
= yield from asyncio
.wait_for(
304 self
.loop
.run_in_executor(
306 self
._plugin
.nfvi_metrics
,
310 timeout
=NfviMetrics
.TIMEOUT
,
314 except asyncio
.TimeoutError
:
315 msg
= "timeout on request for nfvi metrics (vim-id = {})"
316 self
.log
.warning(msg
.format(self
._vim
_id
))
319 except Exception as e
:
320 self
.log
.exception(e
)
324 # Create uninitialized metric structure
325 vdu_metrics
= RwVnfrYang
.YangData_RwProject_Project_VnfrCatalog_Vnfr_Vdur_NfviMetrics()
328 vdu_metrics
.vcpu
.total
= self
.vdur
.vm_flavor
.vcpu_count
329 vdu_metrics
.vcpu
.utilization
= metrics
.vcpu
.utilization
332 vdu_metrics
.memory
.used
= metrics
.memory
.used
333 vdu_metrics
.memory
.total
= self
.vdur
.vm_flavor
.memory_mb
334 vdu_metrics
.memory
.utilization
= 100 * vdu_metrics
.memory
.used
/ vdu_metrics
.memory
.total
338 vdu_metrics
.storage
.used
= metrics
.storage
.used
339 if self
.vdur
.has_field('volumes'):
340 for volume
in self
.vdur
.volumes
:
341 if vdu_metrics
.storage
.total
is None:
342 vdu_metrics
.storage
.total
= 1e9
* volume
.size
344 vdu_metrics
.storage
.total
+= (1e9
* volume
.size
)
346 vdu_metrics
.storage
.total
= 1e9
* self
.vdur
.vm_flavor
.storage_gb
347 utilization
= 100 * vdu_metrics
.storage
.used
/ vdu_metrics
.storage
.total
348 if utilization
> 100:
350 vdu_metrics
.storage
.utilization
= utilization
351 except ZeroDivisionError:
352 vdu_metrics
.storage
.utilization
= 0
355 vdu_metrics
.network
.incoming
.packets
= metrics
.network
.incoming
.packets
356 vdu_metrics
.network
.incoming
.packet_rate
= metrics
.network
.incoming
.packet_rate
357 vdu_metrics
.network
.incoming
.bytes
= metrics
.network
.incoming
.bytes
358 vdu_metrics
.network
.incoming
.byte_rate
= metrics
.network
.incoming
.byte_rate
361 vdu_metrics
.network
.outgoing
.packets
= metrics
.network
.outgoing
.packets
362 vdu_metrics
.network
.outgoing
.packet_rate
= metrics
.network
.outgoing
.packet_rate
363 vdu_metrics
.network
.outgoing
.bytes
= metrics
.network
.outgoing
.bytes
364 vdu_metrics
.network
.outgoing
.byte_rate
= metrics
.network
.outgoing
.byte_rate
367 vdu_metrics
.external_ports
.total
= len([intf
for intf
in self
.vdur
.interface
if intf
.type_yang
== 'EXTERNAL'])
370 vdu_metrics
.internal_ports
.total
= len([intf
for intf
in self
.vdur
.interface
if intf
.type_yang
== 'INTERNAL'])
372 self
._metrics
= vdu_metrics
374 except Exception as e
:
375 self
.log
.exception(e
)
378 # Regardless of the result of the query, we want to make sure that
379 # we do not poll the data source until another sample duration has
381 self
._timestamp
= time
.time()
384 class NfviMetricsCache(object):
385 def __init__(self
, log
, loop
, plugin_manager
):
388 self
._plugin
_manager
= plugin_manager
389 self
._nfvi
_metrics
= dict()
391 self
._vim
_to
_vdur
= dict()
392 self
._vdur
_to
_vim
= dict()
394 def create_entry(self
, account
, vdur
):
395 plugin
= self
._plugin
_manager
.plugin(account
.name
)
396 metrics
= NfviMetrics(self
._log
, self
._loop
, account
, plugin
, vdur
)
397 self
._nfvi
_metrics
[vdur
.vim_id
] = metrics
399 self
._vim
_to
_vdur
[vdur
.vim_id
] = vdur
.id
400 self
._vdur
_to
_vim
[vdur
.id] = vdur
.vim_id
402 def destroy_entry(self
, vdur_id
):
403 vim_id
= self
._vdur
_to
_vim
[vdur_id
]
405 del self
._nfvi
_metrics
[vim_id
]
406 del self
._vdur
_to
_vim
[vdur_id
]
407 del self
._vim
_to
_vdur
[vim_id
]
409 def retrieve(self
, vim_id
):
410 return self
._nfvi
_metrics
[vim_id
].retrieve()
412 def to_vim_id(self
, vdur_id
):
413 return self
._vdur
_to
_vim
[vdur_id
]
415 def to_vdur_id(self
, vim_id
):
416 return self
._vim
_to
_vdur
[vim_id
]
418 def contains_vdur_id(self
, vdur_id
):
419 return vdur_id
in self
._vdur
_to
_vim
421 def contains_vim_id(self
, vim_id
):
422 return vim_id
in self
._vim
_to
_vdur
425 class NfviInterface(object):
427 The NfviInterface serves as an interface for communicating with the
428 underlying infrastructure, i.e. retrieving metrics for VDURs that have been
429 registered with it and managing alarms.
431 The NfviInterface should only need to be invoked using a cloud account and
432 optionally a VIM ID; It should not need to handle mapping from VDUR ID to
436 def __init__(self
, loop
, log
, plugin_manager
, cache
):
437 """Creates an NfviInterface instance
442 plugin_manager - an instance of NfviMetricsPluginManager
443 cache - an instance of NfviMetricsCache
446 self
._executor
= concurrent
.futures
.ThreadPoolExecutor(max_workers
=16)
447 self
._plugin
_manager
= plugin_manager
454 """The event loop used by this NfviInterface"""
459 """The event log used by this NfviInterface"""
464 """The list of metrics contained in this NfviInterface"""
465 return list(self
._cache
._nfvi
_metrics
.values())
467 def nfvi_metrics_available(self
, account
):
468 plugin
= self
._plugin
_manager
.plugin(account
.name
)
469 _
, available
= plugin
.nfvi_metrics_available(account
)
472 def retrieve(self
, vdur_id
):
473 """Returns the NFVI metrics for the specified VDUR
475 Note, a VDUR must be registered with a NfviInterface before
476 metrics can be retrieved for it.
479 vdur_id - the ID of the VDUR to whose metrics should be retrieve
482 An NfviMetrics object for the specified VDUR
485 return self
._cache
.retrieve(self
._cache
.to_vim_id(vdur_id
))
488 def alarm_create(self
, account
, vim_id
, alarm
, timeout
=5):
489 """Create a new alarm
492 account - a CloudAccount instance
493 vim_id - the VM to associate with this alarm
494 alarm - an alarm structure
495 timeout - the request timeout (sec)
498 If the data source does not respond in a timely manner, an
499 asyncio.TimeoutError will be raised.
502 plugin
= self
._plugin
_manager
.plugin(account
.name
)
503 status
= yield from asyncio
.wait_for(
504 self
.loop
.run_in_executor(
506 plugin
.do_alarm_create
,
515 if status
== RwTypes
.RwStatus
.FAILURE
:
516 raise AlarmCreateError()
519 def alarm_destroy(self
, account
, alarm_id
, timeout
=5):
520 """Destroy an existing alarm
523 account - a CloudAccount instance
524 alarm_id - the identifier of the alarm to destroy
525 timeout - the request timeout (sec)
528 If the data source does not respond in a timely manner, an
529 asyncio.TimeoutError will be raised.
532 plugin
= self
._plugin
_manager
.plugin(account
.name
)
533 status
= yield from asyncio
.wait_for(
534 self
.loop
.run_in_executor(
536 plugin
.do_alarm_delete
,
544 if status
== RwTypes
.RwStatus
.FAILURE
:
545 raise AlarmDestroyError()
548 class InstanceConfiguration(object):
550 The InstanceConfiguration class represents configuration information that
551 affects the behavior of the monitor. Essentially this class should contain
552 not functional behavior but serve as a convenient way to share data amongst
553 the components of the monitoring system.
557 self
.polling_period
= None
558 self
.max_polling_frequency
= None
559 self
.min_cache_lifetime
= None
560 self
.public_ip
= None
563 class Monitor(object):
565 The Monitor class is intended to act as a unifying interface for the
566 different sub-systems that are used to monitor the NFVI.
569 def __init__(self
, loop
, log
, config
, project
):
570 """Create a Monitor object
574 log - the logger used by this object
575 config - an instance of InstanceConfiguration
576 project - an instance of the project
581 self
._project
= project
583 self
._cloud
_accounts
= dict()
584 self
._nfvi
_plugins
= NfviMetricsPluginManager(log
)
585 self
._cache
= NfviMetricsCache(log
, loop
, self
._nfvi
_plugins
)
586 self
._nfvi
_interface
= NfviInterface(loop
, log
, self
._nfvi
_plugins
, self
._cache
)
587 self
._config
= config
589 self
._vnfr
_to
_vdurs
= collections
.defaultdict(set)
590 self
._alarms
= collections
.defaultdict(list)
594 """The event loop used by this object"""
599 """The event log used by this object"""
608 """The NFVI metrics cache"""
613 """The list of metrics contained in this Monitor"""
614 return self
._nfvi
_interface
.metrics
616 def nfvi_metrics_available(self
, account
):
617 """Returns a boolean indicating whether NFVI metrics are available
620 account - the name of the cloud account to check
623 a boolean indicating availability of NFVI metrics
626 if account
not in self
._cloud
_accounts
:
629 cloud_account
= self
._cloud
_accounts
[account
]
630 return self
._nfvi
_interface
.nfvi_metrics_available(cloud_account
)
632 def add_cloud_account(self
, account
):
633 """Add a cloud account to the monitor
636 account - a cloud account object
639 If the cloud account has already been added to the monitor, an
640 AccountAlreadyRegisteredError is raised.
643 if account
.name
in self
._cloud
_accounts
:
644 raise AccountAlreadyRegisteredError(account
.name
)
646 self
._cloud
_accounts
[account
.name
] = account
648 if account
.account_type
== "openstack":
649 self
.register_cloud_account(account
, "monasca")
650 elif account
.account_type
== "prop_cloud1":
651 self
.register_cloud_account(account
, "brocade_vcpe")
653 self
.register_cloud_account(account
, "mock")
655 def remove_cloud_account(self
, account_name
):
656 """Remove a cloud account from the monitor
659 account_name - removes the cloud account that has this name
662 If the specified cloud account cannot be found, an
663 UnknownAccountError is raised.
666 if account_name
not in self
._cloud
_accounts
:
667 raise UnknownAccountError()
669 # Make sure that there are no VNFRs associated with this account
670 for vnfr
in self
._vnfrs
.values():
671 if vnfr
.datacenter
== account_name
:
672 raise AccountInUseError()
674 del self
._cloud
_accounts
[account_name
]
675 self
._nfvi
_plugins
.unregister(account_name
)
677 def get_cloud_account(self
, account_name
):
678 """Returns a cloud account by name
681 account_name - the name of the account to return
684 An UnknownAccountError is raised if there is not account object
685 associated with the provided name
688 A cloud account object
691 if account_name
not in self
._cloud
_accounts
:
692 raise UnknownAccountError()
694 return self
._cloud
_accounts
[account_name
]
696 def register_cloud_account(self
, account
, plugin_name
):
697 """Register a cloud account with an NFVI plugin
699 Note that a cloud account can only be registered for one plugin at a
703 account - the cloud account to associate with the plugin
704 plugin_name - the name of the plugin to use
707 self
._nfvi
_plugins
.register(account
, plugin_name
)
709 def add_vnfr(self
, vnfr
):
710 """Add a VNFR to the monitor
716 An UnknownAccountError is raised if the account name contained in
717 the VNFR does not reference a cloud account that has been added to
721 if vnfr
.datacenter
not in self
._cloud
_accounts
:
722 raise UnknownAccountError()
724 account
= self
._cloud
_accounts
[vnfr
.datacenter
]
726 for vdur
in vnfr
.vdur
:
728 self
.add_vdur(account
, vdur
)
729 self
._vnfr
_to
_vdurs
[vnfr
.id].add(vdur
.id)
730 except (VdurMissingVimIdError
, VdurAlreadyRegisteredError
):
733 self
._vnfrs
[vnfr
.id] = vnfr
735 def update_vnfr(self
, vnfr
):
736 """Updates the VNFR information in the monitor
742 An UnknownAccountError is raised if the account name contained in
743 the VNFR does not reference a cloud account that has been added to
747 if vnfr
.datacenter
not in self
._cloud
_accounts
:
748 raise UnknownAccountError()
750 account
= self
._cloud
_accounts
[vnfr
.datacenter
]
752 for vdur
in vnfr
.vdur
:
754 self
.add_vdur(account
, vdur
)
755 self
._vnfr
_to
_vdurs
[vnfr
.id].add(vdur
.id)
756 except (VdurMissingVimIdError
, VdurAlreadyRegisteredError
):
759 def remove_vnfr(self
, vnfr_id
):
760 """Remove a VNFR from the monitor
763 vnfr_id - the ID of the VNFR to remove
766 vdur_ids
= self
._vnfr
_to
_vdurs
[vnfr_id
]
768 for vdur_id
in vdur_ids
:
769 self
.remove_vdur(vdur_id
)
771 del self
._vnfrs
[vnfr_id
]
772 del self
._vnfr
_to
_vdurs
[vnfr_id
]
774 def add_vdur(self
, account
, vdur
):
775 """Adds a VDUR to the monitor
777 Adding a VDUR to the monitor will automatically create a NFVI metrics
778 object that is associated with the VDUR so that the monitor cane
779 provide the NFVI metrics associated with the VDUR.
782 account - the cloud account associated with the VNFR that contains
787 A VdurMissingVimIdError is raised if the provided VDUR does not
788 contain a VIM ID. A VdurAlreadyRegisteredError is raised if the ID
789 associated with the VDUR has already been registered.
793 raise VdurMissingVimIdError(vdur
.id)
795 if self
.is_registered_vdur(vdur
.id):
796 raise VdurAlreadyRegisteredError(vdur
.id)
798 self
.cache
.create_entry(account
, vdur
)
800 def remove_vdur(self
, vdur_id
):
801 """Removes a VDUR from the monitor
804 vdur_id - the ID of the VDUR to remove
807 self
.cache
.destroy_entry(vdur_id
)
809 # Schedule any alarms associated with the VDUR for destruction
810 for account_name
, alarm_id
in self
._alarms
[vdur_id
]:
811 self
.loop
.create_task(self
.destroy_alarm(account_name
, alarm_id
))
813 del self
._alarms
[vdur_id
]
815 def list_vdur(self
, vnfr_id
):
816 """Returns a list of VDURs
819 vnfr_id - the identifier of the VNFR contains the VDURs
825 return self
._vnfrs
[vnfr_id
].vdur
827 def is_registered_vnfr(self
, vnfr_id
):
828 """Returns True if the VNFR is registered with the monitor
831 vnfr_id - the ID of the VNFR to check
834 True if the VNFR is registered and False otherwise.
837 return vnfr_id
in self
._vnfrs
839 def is_registered_vdur(self
, vdur_id
):
840 """Returns True if the VDUR is registered with the monitor
843 vnfr_id - the ID of the VDUR to check
846 True if the VDUR is registered and False otherwise.
849 return self
.cache
.contains_vdur_id(vdur_id
)
851 def retrieve_nfvi_metrics(self
, vdur_id
):
852 """Retrieves the NFVI metrics associated with a VDUR
855 vdur_id - the ID of the VDUR whose metrics are to be retrieved
858 NFVI metrics for a VDUR
861 return self
._nfvi
_interface
.retrieve(vdur_id
)
864 def create_alarm(self
, account_name
, vdur_id
, alarm
):
865 """Create a new alarm
867 This function create an alarm and augments the provided endpoints with
868 endpoints to the launchpad if the launchpad has a public IP. The added
869 endpoints are of the form,
871 http://{host}:4568/{platform}/{vdur_id}/{action}
873 where the 'action' is one of 'ok', 'alarm', or 'insufficient_data'. The
874 messages that are pushed to the launchpad are not defined by RIFT so
875 we need to know which platform an alarm is sent from in order to
880 account_name - the name of the account to use to create the alarm
881 vdur_id - the identifier of the VDUR to associated with the
882 alarm. If the identifier is None, the alarm is not
883 associated with a specific VDUR.
884 alarm - the alarm data
887 account
= self
.get_cloud_account(account_name
)
888 vim_id
= self
.cache
.to_vim_id(vdur_id
)
890 # If the launchpad has a public IP, augment the action webhooks to
891 # include the launchpad so that the alarms can be broadcast as event
893 if self
._config
.public_ip
is not None:
894 url
= "http://{host}:4568/{platform}/{vdur_id}".format(
895 host
=self
._config
.public_ip
,
896 platform
=account
.account_type
,
899 alarm
.actions
.ok
.add().url
= url
+ "/ok"
900 alarm
.actions
.alarm
.add().url
= url
+ "/alarm"
901 alarm
.actions
.alarm
.add().url
= url
+ "/insufficient_data"
903 yield from self
._nfvi
_interface
.alarm_create(account
, vim_id
, alarm
)
905 # Associate the VDUR ID with the alarm ID
906 self
._alarms
[vdur_id
].append((account_name
, alarm
.alarm_id
))
909 def destroy_alarm(self
, account_name
, alarm_id
):
910 """Destroy an existing alarm
913 account_name - the name of the account that owns the alert
914 alarm_id - the identifier of the alarm to destroy
917 account
= self
.get_cloud_account(account_name
)
918 yield from self
._nfvi
_interface
.alarm_destroy(account
, alarm_id
)