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
.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
.CloudAccount(
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
.CloudAccount(
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
.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
.CloudAccount(
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 def test_alarm_create_and_destroy(self
):
344 alarm
= VnfrYang
.YangData_RwProject_Project_VnfrCatalog_Vnfr_Vdur_Alarms()
345 alarm
.name
= "test-alarm"
346 alarm
.description
= "test-description"
347 alarm
.vdur_id
= "test-vdur-id"
348 alarm
.metric
= "CPU_UTILIZATION"
349 alarm
.statistic
= "MINIMUM"
350 alarm
.operation
= "GT"
353 alarm
.evaluations
= 1
355 plugin_impl
= TestNfviInterface
.NfviPluginImpl()
356 plugin
= self
.plugin_manager
.plugin(self
.account
.name
)
357 plugin
.set_impl(plugin_impl
)
359 self
.assertEqual(len(plugin_impl
._alarms
), 0)
362 @wait_for_pending_tasks(self
.loop
)
363 def wait_for_create():
364 coro
= self
.nfvi_interface
.alarm_create(
369 yield from asyncio
.wait_for(
375 self
.loop
.run_until_complete(wait_for_create())
376 self
.assertEqual(len(plugin_impl
._alarms
), 1)
377 self
.assertTrue(alarm
.alarm_id
is not None)
380 @wait_for_pending_tasks(self
.loop
)
381 def wait_for_destroy():
382 coro
= self
.nfvi_interface
.alarm_destroy(
386 yield from asyncio
.wait_for(
392 self
.loop
.run_until_complete(wait_for_destroy())
393 self
.assertEqual(len(plugin_impl
._alarms
), 0)
396 class TestVdurNfviMetrics(unittest
.TestCase
):
398 # Reduce the sample interval so that test run quickly
399 NfviMetrics
.SAMPLE_INTERVAL
= 0.1
401 # Create a mock plugin to define the metrics retrieved. The plugin will
402 # return a VCPU utilization of 0.5.
403 class MockPlugin(object):
405 self
.metrics
= RwmonYang
.NfviMetrics()
407 def nfvi_metrics(self
, account
, vim_id
):
408 self
.metrics
.vcpu
.utilization
= 0.5
411 self
.loop
= asyncio
.get_event_loop()
412 self
.logger
= logging
.getLogger('test-logger')
414 self
.account
= RwcalYang
.CloudAccount(
415 name
='test-cloud-account',
419 # Define the VDUR to avoid division by zero
421 vdur
.vm_flavor
.vcpu_count
= 4
422 vdur
.vm_flavor
.memory_mb
= 100
423 vdur
.vm_flavor
.storage_gb
= 2
424 vdur
.vim_id
= 'test-vim-id'
426 # Instantiate the mock plugin
427 self
.plugin_manager
= NfviMetricsPluginManager(self
.logger
)
428 self
.plugin_manager
.register(self
.account
, "mock")
430 self
.plugin
= self
.plugin_manager
.plugin(self
.account
.name
)
431 self
.plugin
.set_impl(MockPlugin())
433 self
.cache
= NfviMetricsCache(
439 self
.manager
= NfviInterface(
446 self
.metrics
= NfviMetrics(
454 def test_retrieval(self
):
458 # Define a coroutine that can be added to the asyncio event loop
461 # Output from the metrics calls with be written to these nonlocal
466 # This first call will return the current metrics values and
467 # schedule a request to the NFVI to retrieve metrics from the data
468 # source. All metrics will be zero at this point.
469 metrics_a
= self
.metrics
.retrieve()
471 # Wait for the scheduled update to take effect
472 yield from asyncio
.sleep(0.2, loop
=self
.loop
)
474 # Retrieve the updated metrics
475 metrics_b
= self
.metrics
.retrieve()
477 self
.loop
.run_until_complete(update())
479 # Check that the metrics returned indicate that the plugin was queried
480 # and returned the appropriate value, i.e. 0.5 utilization
481 self
.assertEqual(0.0, metrics_a
.vcpu
.utilization
)
482 self
.assertEqual(0.5, metrics_b
.vcpu
.utilization
)
485 class TestNfviMetricsPluginManager(unittest
.TestCase
):
487 self
.logger
= logging
.getLogger('test-logger')
488 self
.plugins
= NfviMetricsPluginManager(self
.logger
)
489 self
.account
= RwcalYang
.CloudAccount(
490 name
='test-cloud-account',
494 def test_mock_plugin(self
):
495 # Register an account name with a mock plugin. If successful, the
496 # plugin manager should return a non-None object.
497 self
.plugins
.register(self
.account
, 'mock')
498 self
.assertIsNotNone(self
.plugins
.plugin(self
.account
.name
))
500 # Now unregister the cloud account
501 self
.plugins
.unregister(self
.account
.name
)
503 # Trying to retrieve a plugin for a cloud account that has not been
504 # registered with the manager is expected to raise an exception.
505 with self
.assertRaises(KeyError):
506 self
.plugins
.plugin(self
.account
.name
)
508 def test_multiple_registration(self
):
509 self
.plugins
.register(self
.account
, 'mock')
511 # Attempting to register the account with another type of plugin will
512 # also cause an exception to be raised.
513 with self
.assertRaises(AccountAlreadyRegisteredError
):
514 self
.plugins
.register(self
.account
, 'mock')
516 # Attempting to register the account with 'openstack' again with cause
517 # an exception to be raised.
518 with self
.assertRaises(AccountAlreadyRegisteredError
):
519 self
.plugins
.register(self
.account
, 'openstack')
521 def test_unsupported_plugin(self
):
522 # If an attempt is made to register a cloud account with an unknown
523 # type of plugin, a PluginNotSupportedError should be raised.
524 with self
.assertRaises(PluginNotSupportedError
):
525 self
.plugins
.register(self
.account
, 'unsupported-plugin')
527 def test_anavailable_plugin(self
):
528 # Create a factory that always raises PluginUnavailableError
529 class UnavailablePluginFactory(PluginFactory
):
530 PLUGIN_NAME
= "unavailable-plugin"
532 def create(self
, cloud_account
):
533 raise PluginUnavailableError()
535 # Register the factory
536 self
.plugins
.register_plugin_factory(UnavailablePluginFactory())
538 # Ensure that the correct exception propagates when the cloud account
540 with self
.assertRaises(PluginUnavailableError
):
541 self
.plugins
.register(self
.account
, "unavailable-plugin")
544 class TestMonitor(unittest
.TestCase
):
546 The Monitor class is the implementation that is called by the
547 MonitorTasklet. It provides the unified interface for controlling and
548 querying the monitoring functionality.
552 # Reduce the sample interval so that test run quickly
553 NfviMetrics
.SAMPLE_INTERVAL
= 0.1
555 self
.loop
= asyncio
.get_event_loop()
556 self
.logger
= logging
.getLogger('test-logger')
557 self
.project
= ManoProject(self
.logger
, name
=DEFAULT_PROJECT
)
558 self
.config
= InstanceConfiguration()
559 self
.monitor
= Monitor(self
.loop
, self
.logger
, self
.config
, self
.project
)
561 self
.account
= RwcalYang
.CloudAccount(
562 name
='test-cloud-account',
566 def test_instance_config(self
):
568 Configuration data for an instance is pass to the Monitor when it is
569 created. The data is passed in the InstanceConfiguration object. This
570 object is typically shared between the tasklet and the monitor, and
571 provides a way for the tasklet to update the configuration of the
574 self
.assertTrue(hasattr(self
.monitor
._config
, "polling_period"))
575 self
.assertTrue(hasattr(self
.monitor
._config
, "min_cache_lifetime"))
576 self
.assertTrue(hasattr(self
.monitor
._config
, "max_polling_frequency"))
578 def test_monitor_cloud_accounts(self
):
580 This test checks the cloud accounts are correctly added and deleted,
581 and that the correct exceptions are raised on duplicate adds or
585 # Add the cloud account to the monitor
586 self
.monitor
.add_cloud_account(self
.account
)
587 self
.assertIn(self
.account
.name
, self
.monitor
._cloud
_accounts
)
589 # Add the cloud account to the monitor again
590 with self
.assertRaises(AccountAlreadyRegisteredError
):
591 self
.monitor
.add_cloud_account(self
.account
)
593 # Delete the cloud account
594 self
.monitor
.remove_cloud_account(self
.account
.name
)
595 self
.assertNotIn(self
.account
.name
, self
.monitor
._cloud
_accounts
)
597 # Delete the cloud account again
598 with self
.assertRaises(UnknownAccountError
):
599 self
.monitor
.remove_cloud_account(self
.account
.name
)
601 def test_monitor_cloud_accounts_illegal_removal(self
):
603 A cloud account may not be removed while there are plugins or records
604 that are associated with it. Attempting to delete such a cloud account
605 will raise an exception.
607 # Add the cloud account to the monitor
608 self
.monitor
.add_cloud_account(self
.account
)
610 # Create a VNFR associated with the cloud account
611 vnfr
= RwVnfrYang
.YangData_RwProject_Project_VnfrCatalog_Vnfr()
612 vnfr
.cloud_account
= self
.account
.name
613 vnfr
.id = 'test-vnfr-id'
615 # Add a VDUR to the VNFR
616 vdur
= vnfr
.vdur
.add()
617 vdur
.vim_id
= 'test-vim-id-1'
618 vdur
.id = 'test-vdur-id-1'
620 # Now add the VNFR to the monitor
621 self
.monitor
.add_vnfr(vnfr
)
623 # Check that the monitor contains the VNFR, VDUR, and metrics
624 self
.assertTrue(self
.monitor
.is_registered_vdur(vdur
.id))
625 self
.assertTrue(self
.monitor
.is_registered_vnfr(vnfr
.id))
626 self
.assertEqual(1, len(self
.monitor
.metrics
))
628 # Deleting the cloud account now should raise an exception because the
629 # VNFR and VDUR are associated with the cloud account.
630 with self
.assertRaises(AccountInUseError
):
631 self
.monitor
.remove_cloud_account(self
.account
.name
)
633 # Now remove the VNFR from the monitor
634 self
.monitor
.remove_vnfr(vnfr
.id)
635 self
.assertFalse(self
.monitor
.is_registered_vdur(vdur
.id))
636 self
.assertFalse(self
.monitor
.is_registered_vnfr(vnfr
.id))
637 self
.assertEqual(0, len(self
.monitor
.metrics
))
639 # Safely delete the cloud account
640 self
.monitor
.remove_cloud_account(self
.account
.name
)
642 def test_vdur_registration(self
):
644 When a VDUR is registered with the Monitor it is registered with the
645 VdurNfviMetricsManager. Thus it is assigned a plugin that can be used
646 to retrieve the NFVI metrics associated with the VDU.
648 # Define the VDUR to be registered
649 vdur
= VnfrYang
.YangData_RwProject_Project_VnfrCatalog_Vnfr_Vdur()
650 vdur
.vm_flavor
.vcpu_count
= 4
651 vdur
.vm_flavor
.memory_mb
= 100
652 vdur
.vm_flavor
.storage_gb
= 2
653 vdur
.vim_id
= 'test-vim-id'
654 vdur
.id = 'test-vdur-id'
656 # Before registering the VDUR, the cloud account needs to be added to
658 self
.monitor
.add_cloud_account(self
.account
)
660 # Register the VDUR with the monitor
661 self
.monitor
.add_vdur(self
.account
, vdur
)
662 self
.assertTrue(self
.monitor
.is_registered_vdur(vdur
.id))
664 # Check that the VDUR has been added to the metrics cache
665 self
.assertTrue(self
.monitor
.cache
.contains_vdur_id(vdur
.id))
667 # Unregister the VDUR
668 self
.monitor
.remove_vdur(vdur
.id)
669 self
.assertFalse(self
.monitor
.is_registered_vdur(vdur
.id))
671 # Check that the VDUR has been removed from the metrics cache
672 self
.assertFalse(self
.monitor
.cache
.contains_vdur_id(vdur
.id))
674 def test_vnfr_add_update_delete(self
):
676 When a VNFR is added to the Monitor a record is created of the
677 relationship between the VNFR and any VDURs that it contains. Each VDUR
678 is then registered with the VdurNfviMetricsManager. A VNFR can also be
679 updated so that it contains more of less VDURs. Any VDURs that are
680 added to the VNFR are registered with the NdurNfviMetricsManager, and
681 any that are removed are unregistered. When a VNFR is deleted, all of
682 the VDURs contained in the VNFR are unregistered.
684 # Define the VDUR to be registered
685 vdur
= RwVnfrYang
.YangData_RwProject_Project_VnfrCatalog_Vnfr_Vdur()
686 vdur
.vim_id
= 'test-vim-id-1'
687 vdur
.id = 'test-vdur-id-1'
689 vnfr
= RwVnfrYang
.YangData_RwProject_Project_VnfrCatalog_Vnfr()
690 vnfr
.cloud_account
= self
.account
.name
691 vnfr
.id = 'test-vnfr-id'
693 vnfr
.vdur
.append(vdur
)
695 self
.monitor
.add_cloud_account(self
.account
)
697 # Add the VNFR to the monitor. This will also register VDURs contained
698 # in the VNFR with the monitor.
699 self
.monitor
.add_vnfr(vnfr
)
700 self
.assertTrue(self
.monitor
.is_registered_vdur('test-vdur-id-1'))
702 # Add another VDUR to the VNFR and update the monitor. Both VDURs
703 # should now be registered
704 vdur
= RwVnfrYang
.YangData_RwProject_Project_VnfrCatalog_Vnfr_Vdur()
705 vdur
.vim_id
= 'test-vim-id-2'
706 vdur
.id = 'test-vdur-id-2'
708 vnfr
.vdur
.append(vdur
)
710 self
.monitor
.update_vnfr(vnfr
)
711 self
.assertTrue(self
.monitor
.is_registered_vdur('test-vdur-id-1'))
712 self
.assertTrue(self
.monitor
.is_registered_vdur('test-vdur-id-2'))
714 # Delete the VNFR from the monitor. This should remove the VNFR and all
715 # of the associated VDURs from the monitor.
716 self
.monitor
.remove_vnfr(vnfr
.id)
717 self
.assertFalse(self
.monitor
.is_registered_vnfr('test-vnfr-id'))
718 self
.assertFalse(self
.monitor
.is_registered_vdur('test-vdur-id-1'))
719 self
.assertFalse(self
.monitor
.is_registered_vdur('test-vdur-id-2'))
721 with self
.assertRaises(KeyError):
722 self
.monitor
.retrieve_nfvi_metrics('test-vdur-id-1')
724 with self
.assertRaises(KeyError):
725 self
.monitor
.retrieve_nfvi_metrics('test-vdur-id-2')
727 def test_complete(self
):
729 This test simulates the addition of a VNFR to the Monitor (along with
730 updates), and retrieves NFVI metrics from the VDUR. The VNFR is then
731 deleted, which should result in a cleanup of all the data in the
735 vnfr
= RwVnfrYang
.YangData_RwProject_Project_VnfrCatalog_Vnfr()
736 vnfr
.cloud_account
= self
.account
.name
737 vnfr
.id = 'test-vnfr-id'
740 vdur
= vnfr
.vdur
.add()
741 vdur
.id = 'test-vdur-id-1'
742 vdur
.vim_id
= 'test-vim-id-1'
743 vdur
.vm_flavor
.vcpu_count
= 4
744 vdur
.vm_flavor
.memory_mb
= 100
745 vdur
.vm_flavor
.storage_gb
= 2
747 vdur
= vnfr
.vdur
.add()
748 vdur
.id = 'test-vdur-id-2'
749 vdur
.vim_id
= 'test-vim-id-2'
750 vdur
.vm_flavor
.vcpu_count
= 4
751 vdur
.vm_flavor
.memory_mb
= 100
752 vdur
.vm_flavor
.storage_gb
= 2
754 class MockPlugin(object):
756 self
._metrics
= dict()
757 self
._metrics
['test-vim-id-1'] = RwmonYang
.NfviMetrics()
758 self
._metrics
['test-vim-id-2'] = RwmonYang
.NfviMetrics()
760 def nfvi_metrics(self
, account
, vim_id
):
761 metrics
= self
._metrics
[vim_id
]
763 if vim_id
== 'test-vim-id-1':
764 metrics
.memory
.used
+= 1000
766 metrics
.memory
.used
+= 2000
770 class MockFactory(PluginFactory
):
773 def create(self
, cloud_account
):
774 plugin
= rw_peas
.PeasPlugin("rwmon_mock", 'RwMon-1.0')
775 impl
= plugin
.get_interface("Monitoring")
776 impl
.set_impl(MockPlugin())
779 # Modify the mock plugin factory
780 self
.monitor
._nfvi
_plugins
._factories
["mock"] = MockFactory()
782 # Add the cloud account the monitor
783 self
.monitor
.add_cloud_account(self
.account
)
785 # Add the VNFR to the monitor.
786 self
.monitor
.add_vnfr(vnfr
)
788 @wait_for_pending_tasks(self
.loop
)
791 # call #1 (time = 0.00s)
792 # The metrics for these VDURs have not been populated yet so a
793 # default metrics object (all zeros) is returned, and a request is
794 # scheduled with the data source to retrieve the metrics.
795 metrics1
= self
.monitor
.retrieve_nfvi_metrics('test-vdur-id-1')
796 metrics2
= self
.monitor
.retrieve_nfvi_metrics('test-vdur-id-2')
798 self
.assertEqual(0, metrics1
.memory
.used
)
799 self
.assertEqual(0, metrics2
.memory
.used
)
801 self
.loop
.run_until_complete(call1())
803 @wait_for_pending_tasks(self
.loop
)
806 # call #2 (wait 0.05s)
807 # The metrics have been populated with data from the data source
808 # due to the request made during call #1.
809 yield from asyncio
.sleep(0.05)
811 metrics1
= self
.monitor
.retrieve_nfvi_metrics('test-vdur-id-1')
812 metrics2
= self
.monitor
.retrieve_nfvi_metrics('test-vdur-id-2')
814 self
.assertEqual(1000, metrics1
.memory
.used
)
815 self
.assertEqual(2000, metrics2
.memory
.used
)
817 self
.loop
.run_until_complete(call2())
819 @wait_for_pending_tasks(self
.loop
)
822 # call #3 (wait 0.50s)
823 # This call exceeds 0.1s (the sample interval of the plugin)
824 # from when the data was retrieved. The cached metrics are
825 # immediately returned, but a request is made to the data source to
826 # refresh these metrics.
827 yield from asyncio
.sleep(0.10)
829 metrics1
= self
.monitor
.retrieve_nfvi_metrics('test-vdur-id-1')
830 metrics2
= self
.monitor
.retrieve_nfvi_metrics('test-vdur-id-2')
832 self
.assertEqual(1000, metrics1
.memory
.used
)
833 self
.assertEqual(2000, metrics2
.memory
.used
)
835 self
.loop
.run_until_complete(call3())
837 @wait_for_pending_tasks(self
.loop
)
840 # call #4 (wait 1.00s)
841 # The metrics retrieved differ from those in call #3 because the
842 # cached metrics have been updated.
843 yield from asyncio
.sleep(0.10)
844 metrics1
= self
.monitor
.retrieve_nfvi_metrics('test-vdur-id-1')
845 metrics2
= self
.monitor
.retrieve_nfvi_metrics('test-vdur-id-2')
847 self
.assertEqual(2000, metrics1
.memory
.used
)
848 self
.assertEqual(4000, metrics2
.memory
.used
)
850 self
.loop
.run_until_complete(call4())
853 def main(argv
=sys
.argv
[1:]):
854 logging
.basicConfig(format
='TEST %(message)s')
856 parser
= argparse
.ArgumentParser()
857 parser
.add_argument('-v', '--verbose', action
='store_true')
859 args
= parser
.parse_args(argv
)
861 # Set the global logging level
862 logging
.getLogger().setLevel(logging
.DEBUG
if args
.verbose
else logging
.ERROR
)
864 # Set the logger in this test to use a null handler
865 logging
.getLogger('test-logger').addHandler(logging
.NullHandler())
867 # The unittest framework requires a program name, so use the name of this
868 # file instead (we do not want to have to pass a fake program name to main
869 # when this is called from the interpreter).
870 unittest
.main(argv
=[__file__
] + argv
,
871 testRunner
=xmlrunner
.XMLTestRunner(
872 output
=os
.environ
["RIFT_MODULE_TEST"]))
874 if __name__
== '__main__':