4 # Copyright 2016 RIFT.IO Inc
6 # Licensed under the Apache License, Version 2.0 (the "License");
7 # you may not use this file except in compliance with the License.
8 # You may obtain a copy of the License at
10 # http://www.apache.org/licenses/LICENSE-2.0
12 # Unless required by applicable law or agreed to in writing, software
13 # distributed under the License is distributed on an "AS IS" BASIS,
14 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 # See the License for the specific language governing permissions and
16 # limitations under the License.
22 import concurrent
.futures
32 gi
.require_version('NsrYang', '1.0')
33 gi
.require_version('RwcalYang', '1.0')
34 gi
.require_version('RwmonYang', '1.0')
35 gi
.require_version('RwVnfrYang', '1.0')
36 gi
.require_version('RwTypes', '1.0')
37 gi
.require_version('RwMon', '1.0')
39 from gi
.repository
import (
48 from rift
.tasklets
.rwmonitor
.core
import (
49 AccountAlreadyRegisteredError
,
51 InstanceConfiguration
,
56 NfviMetricsPluginManager
,
58 PluginNotSupportedError
,
59 PluginUnavailableError
,
63 from rift
.mano
.utils
.project
import ManoProject
, DEFAULT_PROJECT
66 class wait_for_pending_tasks(object):
68 This class defines a decorator that can be used to ensure that any asyncio
69 tasks created as a side-effect of coroutine are allowed to come to
73 def __init__(self
, loop
, timeout
=1):
75 self
.timeout
= timeout
77 def __call__(self
, coro
):
80 original
= self
.pending_tasks()
81 result
= yield from coro()
83 current
= self
.pending_tasks()
84 remaining
= current
- original
87 yield from asyncio
.wait(
97 def pending_tasks(self
):
98 return {t
for t
in asyncio
.Task
.all_tasks(loop
=self
.loop
) if not t
.done()}
101 class MockTasklet(object):
102 def __init__(self
, dts
, log
, loop
, records
):
106 self
.records
= records
107 self
.polling_period
= 0
108 self
.executor
= concurrent
.futures
.ThreadPoolExecutor(max_workers
=16)
111 def make_nsr(ns_instance_config_ref
=str(uuid
.uuid4())):
112 nsr
= NsrYang
.YangData_RwProject_Project_NsInstanceOpdata_Nsr()
113 nsr
.ns_instance_config_ref
= ns_instance_config_ref
116 def make_vnfr(id=str(uuid
.uuid4())):
117 vnfr
= VnfrYang
.YangData_RwProject_Project_VnfrCatalog_Vnfr()
121 def make_vdur(id=str(uuid
.uuid4()), vim_id
=str(uuid
.uuid4())):
122 vdur
= VnfrYang
.YangData_RwProject_Project_VnfrCatalog_Vnfr_Vdur()
128 class TestNfviMetricsCache(unittest
.TestCase
):
129 class Plugin(object):
130 def nfvi_metrics_available(self
, cloud_account
):
133 def nfvi_metrics(self
, account
, vim_id
):
134 metrics
= RwmonYang
.YangData_RwProject_Project_NfviMetrics()
135 metrics
.vcpu
.utilization
= 0.5
139 self
.loop
= asyncio
.new_event_loop()
140 self
.logger
= logging
.getLogger('test-logger')
142 self
.account
= RwcalYang
.YangData_RwProject_Project_CloudAccounts_CloudAccountList(
143 name
='test-cloud-account',
147 self
.plugin_manager
= NfviMetricsPluginManager(self
.logger
)
148 self
.plugin_manager
.register(self
.account
, "mock")
150 mock
= self
.plugin_manager
.plugin(self
.account
.name
)
151 mock
.set_impl(TestNfviMetricsCache
.Plugin())
153 self
.vdur
= VnfrYang
.YangData_RwProject_Project_VnfrCatalog_Vnfr_Vdur()
154 self
.vdur
.id = "test-vdur-id"
155 self
.vdur
.vim_id
= "test-vim-id"
156 self
.vdur
.vm_flavor
.vcpu_count
= 4
157 self
.vdur
.vm_flavor
.memory_mb
= 1
158 self
.vdur
.vm_flavor
.storage_gb
= 1
160 def test_create_destroy_entry(self
):
161 cache
= NfviMetricsCache(self
.logger
, self
.loop
, self
.plugin_manager
)
162 self
.assertEqual(len(cache
._nfvi
_metrics
), 0)
164 cache
.create_entry(self
.account
, self
.vdur
)
165 self
.assertEqual(len(cache
._nfvi
_metrics
), 1)
167 cache
.destroy_entry(self
.vdur
.id)
168 self
.assertEqual(len(cache
._nfvi
_metrics
), 0)
170 def test_retrieve(self
):
171 NfviMetrics
.SAMPLE_INTERVAL
= 1
173 cache
= NfviMetricsCache(self
.logger
, self
.loop
, self
.plugin_manager
)
174 cache
.create_entry(self
.account
, self
.vdur
)
176 @wait_for_pending_tasks(self
.loop
)
178 def retrieve_metrics():
179 metrics
= cache
.retrieve("test-vim-id")
180 self
.assertEqual(metrics
.vcpu
.utilization
, 0.0)
182 yield from asyncio
.sleep(NfviMetrics
.SAMPLE_INTERVAL
, loop
=self
.loop
)
184 metrics
= cache
.retrieve("test-vim-id")
185 self
.assertEqual(metrics
.vcpu
.utilization
, 0.5)
187 self
.loop
.run_until_complete(retrieve_metrics())
189 def test_id_mapping(self
):
190 cache
= NfviMetricsCache(self
.logger
, self
.loop
, self
.plugin_manager
)
192 cache
.create_entry(self
.account
, self
.vdur
)
194 self
.assertEqual(cache
.to_vim_id(self
.vdur
.id), self
.vdur
.vim_id
)
195 self
.assertEqual(cache
.to_vdur_id(self
.vdur
.vim_id
), self
.vdur
.id)
196 self
.assertTrue(cache
.contains_vdur_id(self
.vdur
.id))
197 self
.assertTrue(cache
.contains_vim_id(self
.vdur
.vim_id
))
199 cache
.destroy_entry(self
.vdur
.id)
201 self
.assertFalse(cache
.contains_vdur_id(self
.vdur
.id))
202 self
.assertFalse(cache
.contains_vim_id(self
.vdur
.vim_id
))
205 class TestNfviMetrics(unittest
.TestCase
):
206 class Plugin(object):
207 def nfvi_metrics_available(self
, cloud_account
):
210 def nfvi_metrics(self
, account
, vim_id
):
211 metrics
= RwVnfrYang
.YangData_RwProject_Project_VnfrCatalog_Vnfr_Vdur_NfviMetrics()
212 metrics
.vcpu
.utilization
= 0.5
216 self
.loop
= asyncio
.new_event_loop()
217 self
.account
= RwcalYang
.YangData_RwProject_Project_CloudAccounts_CloudAccountList(
218 name
='test-cloud-account',
222 self
.plugin
= TestNfviMetrics
.Plugin()
223 self
.logger
= logging
.getLogger('test-logger')
225 self
.vdur
= make_vdur()
226 self
.vdur
.vm_flavor
.vcpu_count
= 4
227 self
.vdur
.vm_flavor
.memory_mb
= 100
228 self
.vdur
.vm_flavor
.storage_gb
= 2
229 self
.vdur
.vim_id
= 'test-vim-id'
231 def test_update(self
):
232 nfvi_metrics
= NfviMetrics(
240 # Reduce the SAMPLE_INTERVAL so that the test does not take a long time
241 nfvi_metrics
.SAMPLE_INTERVAL
= 1
243 # The metrics have never been retrieved so they should be updated
244 self
.assertTrue(nfvi_metrics
.should_update())
246 # The metrics return will be empty because the cache version is empty.
247 # However, this trigger an update to retrieve metrics from the plugin.
248 metrics
= nfvi_metrics
.retrieve()
249 self
.assertEqual(metrics
.vcpu
.utilization
, 0.0)
251 # An update has been trigger by the retrieve call so additional updates
253 self
.assertFalse(nfvi_metrics
.should_update())
254 self
.assertFalse(nfvi_metrics
._updating
.done())
256 # Allow the event loop to run until the update is complete
258 @wait_for_pending_tasks(self
.loop
)
259 def wait_for_update():
260 yield from asyncio
.wait_for(
261 nfvi_metrics
._updating
,
266 self
.loop
.run_until_complete(wait_for_update())
268 # Check that we have a new metrics object
269 metrics
= nfvi_metrics
.retrieve()
270 self
.assertEqual(metrics
.vcpu
.utilization
, 0.5)
272 # We have just updated the metrics so it should be unnecessary to update
274 self
.assertFalse(nfvi_metrics
.should_update())
275 self
.assertTrue(nfvi_metrics
._updating
.done())
277 # Wait an amount of time equal to the SAMPLE_INTERVAL. This ensures
278 # that the metrics that were just retrieved become stale...
279 time
.sleep(NfviMetrics
.SAMPLE_INTERVAL
)
281 # ...now it is time to update again
282 self
.assertTrue(nfvi_metrics
.should_update())
285 class TestNfviInterface(unittest
.TestCase
):
286 class NfviPluginImpl(object):
290 def nfvi_metrics(self
, account
, vm_id
):
291 return rwmon
.YangData_RwProject_Project_NfviMetrics()
293 def nfvi_metrics_available(self
, account
):
296 def alarm_create(self
, account
, vim_id
, alarm
):
297 alarm
.alarm_id
= str(uuid
.uuid4())
298 self
._alarms
.add(alarm
.alarm_id
)
299 return RwTypes
.RwStatus
.SUCCESS
301 def alarm_delete(self
, account
, alarm_id
):
302 self
._alarms
.remove(alarm_id
)
303 return RwTypes
.RwStatus
.SUCCESS
306 self
.loop
= asyncio
.new_event_loop()
307 self
.logger
= logging
.getLogger('test-logger')
309 self
.account
= RwcalYang
.YangData_RwProject_Project_CloudAccounts_CloudAccountList(
310 name
='test-cloud-account',
314 # Define the VDUR to avoid division by zero
315 self
.vdur
= make_vdur()
316 self
.vdur
.vm_flavor
.vcpu_count
= 4
317 self
.vdur
.vm_flavor
.memory_mb
= 100
318 self
.vdur
.vm_flavor
.storage_gb
= 2
319 self
.vdur
.vim_id
= 'test-vim-id'
321 self
.plugin_manager
= NfviMetricsPluginManager(self
.logger
)
322 self
.plugin_manager
.register(self
.account
, "mock")
324 self
.cache
= NfviMetricsCache(
330 self
.nfvi_interface
= NfviInterface(
337 def test_nfvi_metrics_available(self
):
338 self
.assertTrue(self
.nfvi_interface
.nfvi_metrics_available(self
.account
))
340 def test_retrieve(self
):
343 @unittest.skip("Alarms are being disabled in monitor")
344 def test_alarm_create_and_destroy(self
):
345 alarm
= VnfrYang
.YangData_RwProject_Project_VnfrCatalog_Vnfr_Vdur_Alarms()
346 alarm
.name
= "test-alarm"
347 alarm
.description
= "test-description"
348 alarm
.vdur_id
= "test-vdur-id"
349 alarm
.metric
= "CPU_UTILIZATION"
350 alarm
.statistic
= "MINIMUM"
351 alarm
.operation
= "GT"
354 alarm
.evaluations
= 1
356 plugin_impl
= TestNfviInterface
.NfviPluginImpl()
357 plugin
= self
.plugin_manager
.plugin(self
.account
.name
)
358 plugin
.set_impl(plugin_impl
)
360 self
.assertEqual(len(plugin_impl
._alarms
), 0)
363 @wait_for_pending_tasks(self
.loop
)
364 def wait_for_create():
365 coro
= self
.nfvi_interface
.alarm_create(
370 yield from asyncio
.wait_for(
376 self
.loop
.run_until_complete(wait_for_create())
377 self
.assertEqual(len(plugin_impl
._alarms
), 1)
378 self
.assertTrue(alarm
.alarm_id
is not None)
381 @wait_for_pending_tasks(self
.loop
)
382 def wait_for_destroy():
383 coro
= self
.nfvi_interface
.alarm_destroy(
387 yield from asyncio
.wait_for(
393 self
.loop
.run_until_complete(wait_for_destroy())
394 self
.assertEqual(len(plugin_impl
._alarms
), 0)
397 class TestVdurNfviMetrics(unittest
.TestCase
):
399 # Reduce the sample interval so that test run quickly
400 NfviMetrics
.SAMPLE_INTERVAL
= 0.1
402 # Create a mock plugin to define the metrics retrieved. The plugin will
403 # return a VCPU utilization of 0.5.
404 class MockPlugin(object):
406 self
.metrics
= RwmonYang
.YangData_RwProject_Project_NfviMetrics()
408 def nfvi_metrics(self
, account
, vim_id
):
409 self
.metrics
.vcpu
.utilization
= 0.5
412 self
.loop
= asyncio
.get_event_loop()
413 self
.logger
= logging
.getLogger('test-logger')
415 self
.account
= RwcalYang
.YangData_RwProject_Project_CloudAccounts_CloudAccountList(
416 name
='test-cloud-account',
420 # Define the VDUR to avoid division by zero
422 vdur
.vm_flavor
.vcpu_count
= 4
423 vdur
.vm_flavor
.memory_mb
= 100
424 vdur
.vm_flavor
.storage_gb
= 2
425 vdur
.vim_id
= 'test-vim-id'
427 # Instantiate the mock plugin
428 self
.plugin_manager
= NfviMetricsPluginManager(self
.logger
)
429 self
.plugin_manager
.register(self
.account
, "mock")
431 self
.plugin
= self
.plugin_manager
.plugin(self
.account
.name
)
432 self
.plugin
.set_impl(MockPlugin())
434 self
.cache
= NfviMetricsCache(
440 self
.manager
= NfviInterface(
447 self
.metrics
= NfviMetrics(
455 def test_retrieval(self
):
459 # Define a coroutine that can be added to the asyncio event loop
462 # Output from the metrics calls with be written to these nonlocal
467 # This first call will return the current metrics values and
468 # schedule a request to the NFVI to retrieve metrics from the data
469 # source. All metrics will be zero at this point.
470 metrics_a
= self
.metrics
.retrieve()
472 # Wait for the scheduled update to take effect
473 yield from asyncio
.sleep(0.2, loop
=self
.loop
)
475 # Retrieve the updated metrics
476 metrics_b
= self
.metrics
.retrieve()
478 self
.loop
.run_until_complete(update())
480 # Check that the metrics returned indicate that the plugin was queried
481 # and returned the appropriate value, i.e. 0.5 utilization
482 self
.assertEqual(0.0, metrics_a
.vcpu
.utilization
)
483 self
.assertEqual(0.5, metrics_b
.vcpu
.utilization
)
486 class TestNfviMetricsPluginManager(unittest
.TestCase
):
488 self
.logger
= logging
.getLogger('test-logger')
489 self
.plugins
= NfviMetricsPluginManager(self
.logger
)
490 self
.account
= RwcalYang
.YangData_RwProject_Project_CloudAccounts_CloudAccountList(
491 name
='test-cloud-account',
495 def test_mock_plugin(self
):
496 # Register an account name with a mock plugin. If successful, the
497 # plugin manager should return a non-None object.
498 self
.plugins
.register(self
.account
, 'mock')
499 self
.assertIsNotNone(self
.plugins
.plugin(self
.account
.name
))
501 # Now unregister the cloud account
502 self
.plugins
.unregister(self
.account
.name
)
504 # Trying to retrieve a plugin for a cloud account that has not been
505 # registered with the manager is expected to raise an exception.
506 with self
.assertRaises(KeyError):
507 self
.plugins
.plugin(self
.account
.name
)
509 def test_multiple_registration(self
):
510 self
.plugins
.register(self
.account
, 'mock')
512 # Attempting to register the account with another type of plugin will
513 # also cause an exception to be raised.
514 with self
.assertRaises(AccountAlreadyRegisteredError
):
515 self
.plugins
.register(self
.account
, 'mock')
517 # Attempting to register the account with 'openstack' again with cause
518 # an exception to be raised.
519 with self
.assertRaises(AccountAlreadyRegisteredError
):
520 self
.plugins
.register(self
.account
, 'openstack')
522 def test_unsupported_plugin(self
):
523 # If an attempt is made to register a cloud account with an unknown
524 # type of plugin, a PluginNotSupportedError should be raised.
525 with self
.assertRaises(PluginNotSupportedError
):
526 self
.plugins
.register(self
.account
, 'unsupported-plugin')
528 def test_anavailable_plugin(self
):
529 # Create a factory that always raises PluginUnavailableError
530 class UnavailablePluginFactory(PluginFactory
):
531 PLUGIN_NAME
= "unavailable-plugin"
533 def create(self
, cloud_account
):
534 raise PluginUnavailableError()
536 # Register the factory
537 self
.plugins
.register_plugin_factory(UnavailablePluginFactory())
539 # Ensure that the correct exception propagates when the cloud account
541 with self
.assertRaises(PluginUnavailableError
):
542 self
.plugins
.register(self
.account
, "unavailable-plugin")
545 class TestMonitor(unittest
.TestCase
):
547 The Monitor class is the implementation that is called by the
548 MonitorTasklet. It provides the unified interface for controlling and
549 querying the monitoring functionality.
553 # Reduce the sample interval so that test run quickly
554 NfviMetrics
.SAMPLE_INTERVAL
= 0.1
556 self
.loop
= asyncio
.get_event_loop()
557 self
.logger
= logging
.getLogger('test-logger')
558 self
.project
= ManoProject(self
.logger
, name
=DEFAULT_PROJECT
)
559 self
.config
= InstanceConfiguration()
560 self
.monitor
= Monitor(self
.loop
, self
.logger
, self
.config
, self
.project
)
562 self
.account
= RwcalYang
.YangData_RwProject_Project_CloudAccounts_CloudAccountList(
563 name
='test-cloud-account',
567 def test_instance_config(self
):
569 Configuration data for an instance is pass to the Monitor when it is
570 created. The data is passed in the InstanceConfiguration object. This
571 object is typically shared between the tasklet and the monitor, and
572 provides a way for the tasklet to update the configuration of the
575 self
.assertTrue(hasattr(self
.monitor
._config
, "polling_period"))
576 self
.assertTrue(hasattr(self
.monitor
._config
, "min_cache_lifetime"))
577 self
.assertTrue(hasattr(self
.monitor
._config
, "max_polling_frequency"))
579 def test_monitor_cloud_accounts(self
):
581 This test checks the cloud accounts are correctly added and deleted,
582 and that the correct exceptions are raised on duplicate adds or
586 # Add the cloud account to the monitor
587 self
.monitor
.add_cloud_account(self
.account
)
588 self
.assertIn(self
.account
.name
, self
.monitor
._cloud
_accounts
)
590 # Add the cloud account to the monitor again
591 with self
.assertRaises(AccountAlreadyRegisteredError
):
592 self
.monitor
.add_cloud_account(self
.account
)
594 # Delete the cloud account
595 self
.monitor
.remove_cloud_account(self
.account
.name
)
596 self
.assertNotIn(self
.account
.name
, self
.monitor
._cloud
_accounts
)
598 # Delete the cloud account again
599 with self
.assertRaises(UnknownAccountError
):
600 self
.monitor
.remove_cloud_account(self
.account
.name
)
602 def test_monitor_cloud_accounts_illegal_removal(self
):
604 A cloud account may not be removed while there are plugins or records
605 that are associated with it. Attempting to delete such a cloud account
606 will raise an exception.
608 # Add the cloud account to the monitor
609 self
.monitor
.add_cloud_account(self
.account
)
611 # Create a VNFR associated with the cloud account
612 vnfr
= RwVnfrYang
.YangData_RwProject_Project_VnfrCatalog_Vnfr()
613 vnfr
.datacenter
= self
.account
.name
614 vnfr
.id = 'test-vnfr-id'
616 # Add a VDUR to the VNFR
617 vdur
= vnfr
.vdur
.add()
618 vdur
.vim_id
= 'test-vim-id-1'
619 vdur
.id = 'test-vdur-id-1'
621 # Now add the VNFR to the monitor
622 self
.monitor
.add_vnfr(vnfr
)
624 # Check that the monitor contains the VNFR, VDUR, and metrics
625 self
.assertTrue(self
.monitor
.is_registered_vdur(vdur
.id))
626 self
.assertTrue(self
.monitor
.is_registered_vnfr(vnfr
.id))
627 self
.assertEqual(1, len(self
.monitor
.metrics
))
629 # Deleting the cloud account now should raise an exception because the
630 # VNFR and VDUR are associated with the cloud account.
631 with self
.assertRaises(AccountInUseError
):
632 self
.monitor
.remove_cloud_account(self
.account
.name
)
634 # Now remove the VNFR from the monitor
635 self
.monitor
.remove_vnfr(vnfr
.id)
636 self
.assertFalse(self
.monitor
.is_registered_vdur(vdur
.id))
637 self
.assertFalse(self
.monitor
.is_registered_vnfr(vnfr
.id))
638 self
.assertEqual(0, len(self
.monitor
.metrics
))
640 # Safely delete the cloud account
641 self
.monitor
.remove_cloud_account(self
.account
.name
)
643 def test_vdur_registration(self
):
645 When a VDUR is registered with the Monitor it is registered with the
646 VdurNfviMetricsManager. Thus it is assigned a plugin that can be used
647 to retrieve the NFVI metrics associated with the VDU.
649 # Define the VDUR to be registered
650 vdur
= VnfrYang
.YangData_RwProject_Project_VnfrCatalog_Vnfr_Vdur()
651 vdur
.vm_flavor
.vcpu_count
= 4
652 vdur
.vm_flavor
.memory_mb
= 100
653 vdur
.vm_flavor
.storage_gb
= 2
654 vdur
.vim_id
= 'test-vim-id'
655 vdur
.id = 'test-vdur-id'
657 # Before registering the VDUR, the cloud account needs to be added to
659 self
.monitor
.add_cloud_account(self
.account
)
661 # Register the VDUR with the monitor
662 self
.monitor
.add_vdur(self
.account
, vdur
)
663 self
.assertTrue(self
.monitor
.is_registered_vdur(vdur
.id))
665 # Check that the VDUR has been added to the metrics cache
666 self
.assertTrue(self
.monitor
.cache
.contains_vdur_id(vdur
.id))
668 # Unregister the VDUR
669 self
.monitor
.remove_vdur(vdur
.id)
670 self
.assertFalse(self
.monitor
.is_registered_vdur(vdur
.id))
672 # Check that the VDUR has been removed from the metrics cache
673 self
.assertFalse(self
.monitor
.cache
.contains_vdur_id(vdur
.id))
675 def test_vnfr_add_update_delete(self
):
677 When a VNFR is added to the Monitor a record is created of the
678 relationship between the VNFR and any VDURs that it contains. Each VDUR
679 is then registered with the VdurNfviMetricsManager. A VNFR can also be
680 updated so that it contains more of less VDURs. Any VDURs that are
681 added to the VNFR are registered with the NdurNfviMetricsManager, and
682 any that are removed are unregistered. When a VNFR is deleted, all of
683 the VDURs contained in the VNFR are unregistered.
685 # Define the VDUR to be registered
686 vdur
= RwVnfrYang
.YangData_RwProject_Project_VnfrCatalog_Vnfr_Vdur()
687 vdur
.vim_id
= 'test-vim-id-1'
688 vdur
.id = 'test-vdur-id-1'
690 vnfr
= RwVnfrYang
.YangData_RwProject_Project_VnfrCatalog_Vnfr()
691 vnfr
.datacenter
= self
.account
.name
692 vnfr
.id = 'test-vnfr-id'
694 vnfr
.vdur
.append(vdur
)
696 self
.monitor
.add_cloud_account(self
.account
)
698 # Add the VNFR to the monitor. This will also register VDURs contained
699 # in the VNFR with the monitor.
700 self
.monitor
.add_vnfr(vnfr
)
701 self
.assertTrue(self
.monitor
.is_registered_vdur('test-vdur-id-1'))
703 # Add another VDUR to the VNFR and update the monitor. Both VDURs
704 # should now be registered
705 vdur
= RwVnfrYang
.YangData_RwProject_Project_VnfrCatalog_Vnfr_Vdur()
706 vdur
.vim_id
= 'test-vim-id-2'
707 vdur
.id = 'test-vdur-id-2'
709 vnfr
.vdur
.append(vdur
)
711 self
.monitor
.update_vnfr(vnfr
)
712 self
.assertTrue(self
.monitor
.is_registered_vdur('test-vdur-id-1'))
713 self
.assertTrue(self
.monitor
.is_registered_vdur('test-vdur-id-2'))
715 # Delete the VNFR from the monitor. This should remove the VNFR and all
716 # of the associated VDURs from the monitor.
717 self
.monitor
.remove_vnfr(vnfr
.id)
718 self
.assertFalse(self
.monitor
.is_registered_vnfr('test-vnfr-id'))
719 self
.assertFalse(self
.monitor
.is_registered_vdur('test-vdur-id-1'))
720 self
.assertFalse(self
.monitor
.is_registered_vdur('test-vdur-id-2'))
722 with self
.assertRaises(KeyError):
723 self
.monitor
.retrieve_nfvi_metrics('test-vdur-id-1')
725 with self
.assertRaises(KeyError):
726 self
.monitor
.retrieve_nfvi_metrics('test-vdur-id-2')
728 def test_complete(self
):
730 This test simulates the addition of a VNFR to the Monitor (along with
731 updates), and retrieves NFVI metrics from the VDUR. The VNFR is then
732 deleted, which should result in a cleanup of all the data in the
736 vnfr
= RwVnfrYang
.YangData_RwProject_Project_VnfrCatalog_Vnfr()
737 vnfr
.datacenter
= self
.account
.name
738 vnfr
.id = 'test-vnfr-id'
741 vdur
= vnfr
.vdur
.add()
742 vdur
.id = 'test-vdur-id-1'
743 vdur
.vim_id
= 'test-vim-id-1'
744 vdur
.vm_flavor
.vcpu_count
= 4
745 vdur
.vm_flavor
.memory_mb
= 100
746 vdur
.vm_flavor
.storage_gb
= 2
748 vdur
= vnfr
.vdur
.add()
749 vdur
.id = 'test-vdur-id-2'
750 vdur
.vim_id
= 'test-vim-id-2'
751 vdur
.vm_flavor
.vcpu_count
= 4
752 vdur
.vm_flavor
.memory_mb
= 100
753 vdur
.vm_flavor
.storage_gb
= 2
755 class MockPlugin(object):
757 self
._metrics
= dict()
758 self
._metrics
['test-vim-id-1'] = RwmonYang
.YangData_RwProject_Project_NfviMetrics()
759 self
._metrics
['test-vim-id-2'] = RwmonYang
.YangData_RwProject_Project_NfviMetrics()
761 def nfvi_metrics(self
, account
, vim_id
):
762 metrics
= self
._metrics
[vim_id
]
764 if vim_id
== 'test-vim-id-1':
765 metrics
.memory
.used
+= 1000
767 metrics
.memory
.used
+= 2000
771 class MockFactory(PluginFactory
):
774 def create(self
, cloud_account
):
775 plugin
= rw_peas
.PeasPlugin("rwmon_mock", 'RwMon-1.0')
776 impl
= plugin
.get_interface("Monitoring")
777 impl
.set_impl(MockPlugin())
780 # Modify the mock plugin factory
781 self
.monitor
._nfvi
_plugins
._factories
["mock"] = MockFactory()
783 # Add the cloud account the monitor
784 self
.monitor
.add_cloud_account(self
.account
)
786 # Add the VNFR to the monitor.
787 self
.monitor
.add_vnfr(vnfr
)
789 @wait_for_pending_tasks(self
.loop
)
792 # call #1 (time = 0.00s)
793 # The metrics for these VDURs have not been populated yet so a
794 # default metrics object (all zeros) is returned, and a request is
795 # scheduled with the data source to retrieve the metrics.
796 metrics1
= self
.monitor
.retrieve_nfvi_metrics('test-vdur-id-1')
797 metrics2
= self
.monitor
.retrieve_nfvi_metrics('test-vdur-id-2')
799 self
.assertEqual(0, metrics1
.memory
.used
)
800 self
.assertEqual(0, metrics2
.memory
.used
)
802 self
.loop
.run_until_complete(call1())
804 @wait_for_pending_tasks(self
.loop
)
807 # call #2 (wait 0.05s)
808 # The metrics have been populated with data from the data source
809 # due to the request made during call #1.
810 yield from asyncio
.sleep(0.05)
812 metrics1
= self
.monitor
.retrieve_nfvi_metrics('test-vdur-id-1')
813 metrics2
= self
.monitor
.retrieve_nfvi_metrics('test-vdur-id-2')
815 self
.assertEqual(1000, metrics1
.memory
.used
)
816 self
.assertEqual(2000, metrics2
.memory
.used
)
818 self
.loop
.run_until_complete(call2())
820 @wait_for_pending_tasks(self
.loop
)
823 # call #3 (wait 0.50s)
824 # This call exceeds 0.1s (the sample interval of the plugin)
825 # from when the data was retrieved. The cached metrics are
826 # immediately returned, but a request is made to the data source to
827 # refresh these metrics.
828 yield from asyncio
.sleep(0.10)
830 metrics1
= self
.monitor
.retrieve_nfvi_metrics('test-vdur-id-1')
831 metrics2
= self
.monitor
.retrieve_nfvi_metrics('test-vdur-id-2')
833 self
.assertEqual(1000, metrics1
.memory
.used
)
834 self
.assertEqual(2000, metrics2
.memory
.used
)
836 self
.loop
.run_until_complete(call3())
838 @wait_for_pending_tasks(self
.loop
)
841 # call #4 (wait 1.00s)
842 # The metrics retrieved differ from those in call #3 because the
843 # cached metrics have been updated.
844 yield from asyncio
.sleep(0.10)
845 metrics1
= self
.monitor
.retrieve_nfvi_metrics('test-vdur-id-1')
846 metrics2
= self
.monitor
.retrieve_nfvi_metrics('test-vdur-id-2')
848 self
.assertEqual(2000, metrics1
.memory
.used
)
849 self
.assertEqual(4000, metrics2
.memory
.used
)
851 self
.loop
.run_until_complete(call4())
854 def main(argv
=sys
.argv
[1:]):
855 logging
.basicConfig(format
='TEST %(message)s')
857 parser
= argparse
.ArgumentParser()
858 parser
.add_argument('-v', '--verbose', action
='store_true')
860 args
= parser
.parse_args(argv
)
862 # Set the global logging level
863 logging
.getLogger().setLevel(logging
.DEBUG
if args
.verbose
else logging
.ERROR
)
865 # Set the logger in this test to use a null handler
866 logging
.getLogger('test-logger').addHandler(logging
.NullHandler())
868 # The unittest framework requires a program name, so use the name of this
869 # file instead (we do not want to have to pass a fake program name to main
870 # when this is called from the interpreter).
871 unittest
.main(argv
=[__file__
] + argv
,
872 testRunner
=xmlrunner
.XMLTestRunner(
873 output
=os
.environ
["RIFT_MODULE_TEST"]))
875 if __name__
== '__main__':