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
,
65 class wait_for_pending_tasks(object):
67 This class defines a decorator that can be used to ensure that any asyncio
68 tasks created as a side-effect of coroutine are allowed to come to
72 def __init__(self
, loop
, timeout
=1):
74 self
.timeout
= timeout
76 def __call__(self
, coro
):
79 original
= self
.pending_tasks()
80 result
= yield from coro()
82 current
= self
.pending_tasks()
83 remaining
= current
- original
86 yield from asyncio
.wait(
96 def pending_tasks(self
):
97 return {t
for t
in asyncio
.Task
.all_tasks(loop
=self
.loop
) if not t
.done()}
100 class MockTasklet(object):
101 def __init__(self
, dts
, log
, loop
, records
):
105 self
.records
= records
106 self
.polling_period
= 0
107 self
.executor
= concurrent
.futures
.ThreadPoolExecutor(max_workers
=16)
110 def make_nsr(ns_instance_config_ref
=str(uuid
.uuid4())):
111 nsr
= NsrYang
.YangData_RwProject_Project_NsInstanceOpdata_Nsr()
112 nsr
.ns_instance_config_ref
= ns_instance_config_ref
115 def make_vnfr(id=str(uuid
.uuid4())):
116 vnfr
= VnfrYang
.YangData_RwProject_Project_VnfrCatalog_Vnfr()
120 def make_vdur(id=str(uuid
.uuid4()), vim_id
=str(uuid
.uuid4())):
121 vdur
= VnfrYang
.YangData_RwProject_Project_VnfrCatalog_Vnfr_Vdur()
127 class TestNfviMetricsCache(unittest
.TestCase
):
128 class Plugin(object):
129 def nfvi_metrics_available(self
, cloud_account
):
132 def nfvi_metrics(self
, account
, vim_id
):
133 metrics
= RwmonYang
.NfviMetrics()
134 metrics
.vcpu
.utilization
= 0.5
138 self
.loop
= asyncio
.new_event_loop()
139 self
.logger
= logging
.getLogger('test-logger')
141 self
.account
= RwcalYang
.CloudAccount(
142 name
='test-cloud-account',
146 self
.plugin_manager
= NfviMetricsPluginManager(self
.logger
)
147 self
.plugin_manager
.register(self
.account
, "mock")
149 mock
= self
.plugin_manager
.plugin(self
.account
.name
)
150 mock
.set_impl(TestNfviMetricsCache
.Plugin())
152 self
.vdur
= VnfrYang
.YangData_RwProject_Project_VnfrCatalog_Vnfr_Vdur()
153 self
.vdur
.id = "test-vdur-id"
154 self
.vdur
.vim_id
= "test-vim-id"
155 self
.vdur
.vm_flavor
.vcpu_count
= 4
156 self
.vdur
.vm_flavor
.memory_mb
= 1
157 self
.vdur
.vm_flavor
.storage_gb
= 1
159 def test_create_destroy_entry(self
):
160 cache
= NfviMetricsCache(self
.logger
, self
.loop
, self
.plugin_manager
)
161 self
.assertEqual(len(cache
._nfvi
_metrics
), 0)
163 cache
.create_entry(self
.account
, self
.vdur
)
164 self
.assertEqual(len(cache
._nfvi
_metrics
), 1)
166 cache
.destroy_entry(self
.vdur
.id)
167 self
.assertEqual(len(cache
._nfvi
_metrics
), 0)
169 def test_retrieve(self
):
170 NfviMetrics
.SAMPLE_INTERVAL
= 1
172 cache
= NfviMetricsCache(self
.logger
, self
.loop
, self
.plugin_manager
)
173 cache
.create_entry(self
.account
, self
.vdur
)
175 @wait_for_pending_tasks(self
.loop
)
177 def retrieve_metrics():
178 metrics
= cache
.retrieve("test-vim-id")
179 self
.assertEqual(metrics
.vcpu
.utilization
, 0.0)
181 yield from asyncio
.sleep(NfviMetrics
.SAMPLE_INTERVAL
, loop
=self
.loop
)
183 metrics
= cache
.retrieve("test-vim-id")
184 self
.assertEqual(metrics
.vcpu
.utilization
, 0.5)
186 self
.loop
.run_until_complete(retrieve_metrics())
188 def test_id_mapping(self
):
189 cache
= NfviMetricsCache(self
.logger
, self
.loop
, self
.plugin_manager
)
191 cache
.create_entry(self
.account
, self
.vdur
)
193 self
.assertEqual(cache
.to_vim_id(self
.vdur
.id), self
.vdur
.vim_id
)
194 self
.assertEqual(cache
.to_vdur_id(self
.vdur
.vim_id
), self
.vdur
.id)
195 self
.assertTrue(cache
.contains_vdur_id(self
.vdur
.id))
196 self
.assertTrue(cache
.contains_vim_id(self
.vdur
.vim_id
))
198 cache
.destroy_entry(self
.vdur
.id)
200 self
.assertFalse(cache
.contains_vdur_id(self
.vdur
.id))
201 self
.assertFalse(cache
.contains_vim_id(self
.vdur
.vim_id
))
204 class TestNfviMetrics(unittest
.TestCase
):
205 class Plugin(object):
206 def nfvi_metrics_available(self
, cloud_account
):
209 def nfvi_metrics(self
, account
, vim_id
):
210 metrics
= RwVnfrYang
.YangData_RwProject_Project_VnfrCatalog_Vnfr_Vdur_NfviMetrics()
211 metrics
.vcpu
.utilization
= 0.5
215 self
.loop
= asyncio
.new_event_loop()
216 self
.account
= RwcalYang
.CloudAccount(
217 name
='test-cloud-account',
221 self
.plugin
= TestNfviMetrics
.Plugin()
222 self
.logger
= logging
.getLogger('test-logger')
224 self
.vdur
= make_vdur()
225 self
.vdur
.vm_flavor
.vcpu_count
= 4
226 self
.vdur
.vm_flavor
.memory_mb
= 100
227 self
.vdur
.vm_flavor
.storage_gb
= 2
228 self
.vdur
.vim_id
= 'test-vim-id'
230 def test_update(self
):
231 nfvi_metrics
= NfviMetrics(
239 # Reduce the SAMPLE_INTERVAL so that the test does not take a long time
240 nfvi_metrics
.SAMPLE_INTERVAL
= 1
242 # The metrics have never been retrieved so they should be updated
243 self
.assertTrue(nfvi_metrics
.should_update())
245 # The metrics return will be empty because the cache version is empty.
246 # However, this trigger an update to retrieve metrics from the plugin.
247 metrics
= nfvi_metrics
.retrieve()
248 self
.assertEqual(metrics
.vcpu
.utilization
, 0.0)
250 # An update has been trigger by the retrieve call so additional updates
252 self
.assertFalse(nfvi_metrics
.should_update())
253 self
.assertFalse(nfvi_metrics
._updating
.done())
255 # Allow the event loop to run until the update is complete
257 @wait_for_pending_tasks(self
.loop
)
258 def wait_for_update():
259 yield from asyncio
.wait_for(
260 nfvi_metrics
._updating
,
265 self
.loop
.run_until_complete(wait_for_update())
267 # Check that we have a new metrics object
268 metrics
= nfvi_metrics
.retrieve()
269 self
.assertEqual(metrics
.vcpu
.utilization
, 0.5)
271 # We have just updated the metrics so it should be unnecessary to update
273 self
.assertFalse(nfvi_metrics
.should_update())
274 self
.assertTrue(nfvi_metrics
._updating
.done())
276 # Wait an amount of time equal to the SAMPLE_INTERVAL. This ensures
277 # that the metrics that were just retrieved become stale...
278 time
.sleep(NfviMetrics
.SAMPLE_INTERVAL
)
280 # ...now it is time to update again
281 self
.assertTrue(nfvi_metrics
.should_update())
284 class TestNfviInterface(unittest
.TestCase
):
285 class NfviPluginImpl(object):
289 def nfvi_metrics(self
, account
, vm_id
):
290 return rwmon
.NfviMetrics()
292 def nfvi_metrics_available(self
, account
):
295 def alarm_create(self
, account
, vim_id
, alarm
):
296 alarm
.alarm_id
= str(uuid
.uuid4())
297 self
._alarms
.add(alarm
.alarm_id
)
298 return RwTypes
.RwStatus
.SUCCESS
300 def alarm_delete(self
, account
, alarm_id
):
301 self
._alarms
.remove(alarm_id
)
302 return RwTypes
.RwStatus
.SUCCESS
305 self
.loop
= asyncio
.new_event_loop()
306 self
.logger
= logging
.getLogger('test-logger')
308 self
.account
= RwcalYang
.CloudAccount(
309 name
='test-cloud-account',
313 # Define the VDUR to avoid division by zero
314 self
.vdur
= make_vdur()
315 self
.vdur
.vm_flavor
.vcpu_count
= 4
316 self
.vdur
.vm_flavor
.memory_mb
= 100
317 self
.vdur
.vm_flavor
.storage_gb
= 2
318 self
.vdur
.vim_id
= 'test-vim-id'
320 self
.plugin_manager
= NfviMetricsPluginManager(self
.logger
)
321 self
.plugin_manager
.register(self
.account
, "mock")
323 self
.cache
= NfviMetricsCache(
329 self
.nfvi_interface
= NfviInterface(
336 def test_nfvi_metrics_available(self
):
337 self
.assertTrue(self
.nfvi_interface
.nfvi_metrics_available(self
.account
))
339 def test_retrieve(self
):
342 def test_alarm_create_and_destroy(self
):
343 alarm
= VnfrYang
.YangData_RwProject_Project_VnfrCatalog_Vnfr_Vdur_Alarms()
344 alarm
.name
= "test-alarm"
345 alarm
.description
= "test-description"
346 alarm
.vdur_id
= "test-vdur-id"
347 alarm
.metric
= "CPU_UTILIZATION"
348 alarm
.statistic
= "MINIMUM"
349 alarm
.operation
= "GT"
352 alarm
.evaluations
= 1
354 plugin_impl
= TestNfviInterface
.NfviPluginImpl()
355 plugin
= self
.plugin_manager
.plugin(self
.account
.name
)
356 plugin
.set_impl(plugin_impl
)
358 self
.assertEqual(len(plugin_impl
._alarms
), 0)
361 @wait_for_pending_tasks(self
.loop
)
362 def wait_for_create():
363 coro
= self
.nfvi_interface
.alarm_create(
368 yield from asyncio
.wait_for(
374 self
.loop
.run_until_complete(wait_for_create())
375 self
.assertEqual(len(plugin_impl
._alarms
), 1)
376 self
.assertTrue(alarm
.alarm_id
is not None)
379 @wait_for_pending_tasks(self
.loop
)
380 def wait_for_destroy():
381 coro
= self
.nfvi_interface
.alarm_destroy(
385 yield from asyncio
.wait_for(
391 self
.loop
.run_until_complete(wait_for_destroy())
392 self
.assertEqual(len(plugin_impl
._alarms
), 0)
395 class TestVdurNfviMetrics(unittest
.TestCase
):
397 # Reduce the sample interval so that test run quickly
398 NfviMetrics
.SAMPLE_INTERVAL
= 0.1
400 # Create a mock plugin to define the metrics retrieved. The plugin will
401 # return a VCPU utilization of 0.5.
402 class MockPlugin(object):
404 self
.metrics
= RwmonYang
.NfviMetrics()
406 def nfvi_metrics(self
, account
, vim_id
):
407 self
.metrics
.vcpu
.utilization
= 0.5
410 self
.loop
= asyncio
.get_event_loop()
411 self
.logger
= logging
.getLogger('test-logger')
413 self
.account
= RwcalYang
.CloudAccount(
414 name
='test-cloud-account',
418 # Define the VDUR to avoid division by zero
420 vdur
.vm_flavor
.vcpu_count
= 4
421 vdur
.vm_flavor
.memory_mb
= 100
422 vdur
.vm_flavor
.storage_gb
= 2
423 vdur
.vim_id
= 'test-vim-id'
425 # Instantiate the mock plugin
426 self
.plugin_manager
= NfviMetricsPluginManager(self
.logger
)
427 self
.plugin_manager
.register(self
.account
, "mock")
429 self
.plugin
= self
.plugin_manager
.plugin(self
.account
.name
)
430 self
.plugin
.set_impl(MockPlugin())
432 self
.cache
= NfviMetricsCache(
438 self
.manager
= NfviInterface(
445 self
.metrics
= NfviMetrics(
453 def test_retrieval(self
):
457 # Define a coroutine that can be added to the asyncio event loop
460 # Output from the metrics calls with be written to these nonlocal
465 # This first call will return the current metrics values and
466 # schedule a request to the NFVI to retrieve metrics from the data
467 # source. All metrics will be zero at this point.
468 metrics_a
= self
.metrics
.retrieve()
470 # Wait for the scheduled update to take effect
471 yield from asyncio
.sleep(0.2, loop
=self
.loop
)
473 # Retrieve the updated metrics
474 metrics_b
= self
.metrics
.retrieve()
476 self
.loop
.run_until_complete(update())
478 # Check that the metrics returned indicate that the plugin was queried
479 # and returned the appropriate value, i.e. 0.5 utilization
480 self
.assertEqual(0.0, metrics_a
.vcpu
.utilization
)
481 self
.assertEqual(0.5, metrics_b
.vcpu
.utilization
)
484 class TestNfviMetricsPluginManager(unittest
.TestCase
):
486 self
.logger
= logging
.getLogger('test-logger')
487 self
.plugins
= NfviMetricsPluginManager(self
.logger
)
488 self
.account
= RwcalYang
.CloudAccount(
489 name
='test-cloud-account',
493 def test_mock_plugin(self
):
494 # Register an account name with a mock plugin. If successful, the
495 # plugin manager should return a non-None object.
496 self
.plugins
.register(self
.account
, 'mock')
497 self
.assertIsNotNone(self
.plugins
.plugin(self
.account
.name
))
499 # Now unregister the cloud account
500 self
.plugins
.unregister(self
.account
.name
)
502 # Trying to retrieve a plugin for a cloud account that has not been
503 # registered with the manager is expected to raise an exception.
504 with self
.assertRaises(KeyError):
505 self
.plugins
.plugin(self
.account
.name
)
507 def test_multiple_registration(self
):
508 self
.plugins
.register(self
.account
, 'mock')
510 # Attempting to register the account with another type of plugin will
511 # also cause an exception to be raised.
512 with self
.assertRaises(AccountAlreadyRegisteredError
):
513 self
.plugins
.register(self
.account
, 'mock')
515 # Attempting to register the account with 'openstack' again with cause
516 # an exception to be raised.
517 with self
.assertRaises(AccountAlreadyRegisteredError
):
518 self
.plugins
.register(self
.account
, 'openstack')
520 def test_unsupported_plugin(self
):
521 # If an attempt is made to register a cloud account with an unknown
522 # type of plugin, a PluginNotSupportedError should be raised.
523 with self
.assertRaises(PluginNotSupportedError
):
524 self
.plugins
.register(self
.account
, 'unsupported-plugin')
526 def test_anavailable_plugin(self
):
527 # Create a factory that always raises PluginUnavailableError
528 class UnavailablePluginFactory(PluginFactory
):
529 PLUGIN_NAME
= "unavailable-plugin"
531 def create(self
, cloud_account
):
532 raise PluginUnavailableError()
534 # Register the factory
535 self
.plugins
.register_plugin_factory(UnavailablePluginFactory())
537 # Ensure that the correct exception propagates when the cloud account
539 with self
.assertRaises(PluginUnavailableError
):
540 self
.plugins
.register(self
.account
, "unavailable-plugin")
543 class TestMonitor(unittest
.TestCase
):
545 The Monitor class is the implementation that is called by the
546 MonitorTasklet. It provides the unified interface for controlling and
547 querying the monitoring functionality.
551 # Reduce the sample interval so that test run quickly
552 NfviMetrics
.SAMPLE_INTERVAL
= 0.1
554 self
.loop
= asyncio
.get_event_loop()
555 self
.logger
= logging
.getLogger('test-logger')
556 self
.config
= InstanceConfiguration()
557 self
.monitor
= Monitor(self
.loop
, self
.logger
, self
.config
)
559 self
.account
= RwcalYang
.CloudAccount(
560 name
='test-cloud-account',
564 def test_instance_config(self
):
566 Configuration data for an instance is pass to the Monitor when it is
567 created. The data is passed in the InstanceConfiguration object. This
568 object is typically shared between the tasklet and the monitor, and
569 provides a way for the tasklet to update the configuration of the
572 self
.assertTrue(hasattr(self
.monitor
._config
, "polling_period"))
573 self
.assertTrue(hasattr(self
.monitor
._config
, "min_cache_lifetime"))
574 self
.assertTrue(hasattr(self
.monitor
._config
, "max_polling_frequency"))
576 def test_monitor_cloud_accounts(self
):
578 This test checks the cloud accounts are correctly added and deleted,
579 and that the correct exceptions are raised on duplicate adds or
583 # Add the cloud account to the monitor
584 self
.monitor
.add_cloud_account(self
.account
)
585 self
.assertIn(self
.account
.name
, self
.monitor
._cloud
_accounts
)
587 # Add the cloud account to the monitor again
588 with self
.assertRaises(AccountAlreadyRegisteredError
):
589 self
.monitor
.add_cloud_account(self
.account
)
591 # Delete the cloud account
592 self
.monitor
.remove_cloud_account(self
.account
.name
)
593 self
.assertNotIn(self
.account
.name
, self
.monitor
._cloud
_accounts
)
595 # Delete the cloud account again
596 with self
.assertRaises(UnknownAccountError
):
597 self
.monitor
.remove_cloud_account(self
.account
.name
)
599 def test_monitor_cloud_accounts_illegal_removal(self
):
601 A cloud account may not be removed while there are plugins or records
602 that are associated with it. Attempting to delete such a cloud account
603 will raise an exception.
605 # Add the cloud account to the monitor
606 self
.monitor
.add_cloud_account(self
.account
)
608 # Create a VNFR associated with the cloud account
609 vnfr
= RwVnfrYang
.YangData_RwProject_Project_VnfrCatalog_Vnfr()
610 vnfr
.cloud_account
= self
.account
.name
611 vnfr
.id = 'test-vnfr-id'
613 # Add a VDUR to the VNFR
614 vdur
= vnfr
.vdur
.add()
615 vdur
.vim_id
= 'test-vim-id-1'
616 vdur
.id = 'test-vdur-id-1'
618 # Now add the VNFR to the monitor
619 self
.monitor
.add_vnfr(vnfr
)
621 # Check that the monitor contains the VNFR, VDUR, and metrics
622 self
.assertTrue(self
.monitor
.is_registered_vdur(vdur
.id))
623 self
.assertTrue(self
.monitor
.is_registered_vnfr(vnfr
.id))
624 self
.assertEqual(1, len(self
.monitor
.metrics
))
626 # Deleting the cloud account now should raise an exception because the
627 # VNFR and VDUR are associated with the cloud account.
628 with self
.assertRaises(AccountInUseError
):
629 self
.monitor
.remove_cloud_account(self
.account
.name
)
631 # Now remove the VNFR from the monitor
632 self
.monitor
.remove_vnfr(vnfr
.id)
633 self
.assertFalse(self
.monitor
.is_registered_vdur(vdur
.id))
634 self
.assertFalse(self
.monitor
.is_registered_vnfr(vnfr
.id))
635 self
.assertEqual(0, len(self
.monitor
.metrics
))
637 # Safely delete the cloud account
638 self
.monitor
.remove_cloud_account(self
.account
.name
)
640 def test_vdur_registration(self
):
642 When a VDUR is registered with the Monitor it is registered with the
643 VdurNfviMetricsManager. Thus it is assigned a plugin that can be used
644 to retrieve the NFVI metrics associated with the VDU.
646 # Define the VDUR to be registered
647 vdur
= VnfrYang
.YangData_RwProject_Project_VnfrCatalog_Vnfr_Vdur()
648 vdur
.vm_flavor
.vcpu_count
= 4
649 vdur
.vm_flavor
.memory_mb
= 100
650 vdur
.vm_flavor
.storage_gb
= 2
651 vdur
.vim_id
= 'test-vim-id'
652 vdur
.id = 'test-vdur-id'
654 # Before registering the VDUR, the cloud account needs to be added to
656 self
.monitor
.add_cloud_account(self
.account
)
658 # Register the VDUR with the monitor
659 self
.monitor
.add_vdur(self
.account
, vdur
)
660 self
.assertTrue(self
.monitor
.is_registered_vdur(vdur
.id))
662 # Check that the VDUR has been added to the metrics cache
663 self
.assertTrue(self
.monitor
.cache
.contains_vdur_id(vdur
.id))
665 # Unregister the VDUR
666 self
.monitor
.remove_vdur(vdur
.id)
667 self
.assertFalse(self
.monitor
.is_registered_vdur(vdur
.id))
669 # Check that the VDUR has been removed from the metrics cache
670 self
.assertFalse(self
.monitor
.cache
.contains_vdur_id(vdur
.id))
672 def test_vnfr_add_update_delete(self
):
674 When a VNFR is added to the Monitor a record is created of the
675 relationship between the VNFR and any VDURs that it contains. Each VDUR
676 is then registered with the VdurNfviMetricsManager. A VNFR can also be
677 updated so that it contains more of less VDURs. Any VDURs that are
678 added to the VNFR are registered with the NdurNfviMetricsManager, and
679 any that are removed are unregistered. When a VNFR is deleted, all of
680 the VDURs contained in the VNFR are unregistered.
682 # Define the VDUR to be registered
683 vdur
= RwVnfrYang
.YangData_RwProject_Project_VnfrCatalog_Vnfr_Vdur()
684 vdur
.vim_id
= 'test-vim-id-1'
685 vdur
.id = 'test-vdur-id-1'
687 vnfr
= RwVnfrYang
.YangData_RwProject_Project_VnfrCatalog_Vnfr()
688 vnfr
.cloud_account
= self
.account
.name
689 vnfr
.id = 'test-vnfr-id'
691 vnfr
.vdur
.append(vdur
)
693 self
.monitor
.add_cloud_account(self
.account
)
695 # Add the VNFR to the monitor. This will also register VDURs contained
696 # in the VNFR with the monitor.
697 self
.monitor
.add_vnfr(vnfr
)
698 self
.assertTrue(self
.monitor
.is_registered_vdur('test-vdur-id-1'))
700 # Add another VDUR to the VNFR and update the monitor. Both VDURs
701 # should now be registered
702 vdur
= RwVnfrYang
.YangData_RwProject_Project_VnfrCatalog_Vnfr_Vdur()
703 vdur
.vim_id
= 'test-vim-id-2'
704 vdur
.id = 'test-vdur-id-2'
706 vnfr
.vdur
.append(vdur
)
708 self
.monitor
.update_vnfr(vnfr
)
709 self
.assertTrue(self
.monitor
.is_registered_vdur('test-vdur-id-1'))
710 self
.assertTrue(self
.monitor
.is_registered_vdur('test-vdur-id-2'))
712 # Delete the VNFR from the monitor. This should remove the VNFR and all
713 # of the associated VDURs from the monitor.
714 self
.monitor
.remove_vnfr(vnfr
.id)
715 self
.assertFalse(self
.monitor
.is_registered_vnfr('test-vnfr-id'))
716 self
.assertFalse(self
.monitor
.is_registered_vdur('test-vdur-id-1'))
717 self
.assertFalse(self
.monitor
.is_registered_vdur('test-vdur-id-2'))
719 with self
.assertRaises(KeyError):
720 self
.monitor
.retrieve_nfvi_metrics('test-vdur-id-1')
722 with self
.assertRaises(KeyError):
723 self
.monitor
.retrieve_nfvi_metrics('test-vdur-id-2')
725 def test_complete(self
):
727 This test simulates the addition of a VNFR to the Monitor (along with
728 updates), and retrieves NFVI metrics from the VDUR. The VNFR is then
729 deleted, which should result in a cleanup of all the data in the
733 vnfr
= RwVnfrYang
.YangData_RwProject_Project_VnfrCatalog_Vnfr()
734 vnfr
.cloud_account
= self
.account
.name
735 vnfr
.id = 'test-vnfr-id'
738 vdur
= vnfr
.vdur
.add()
739 vdur
.id = 'test-vdur-id-1'
740 vdur
.vim_id
= 'test-vim-id-1'
741 vdur
.vm_flavor
.vcpu_count
= 4
742 vdur
.vm_flavor
.memory_mb
= 100
743 vdur
.vm_flavor
.storage_gb
= 2
745 vdur
= vnfr
.vdur
.add()
746 vdur
.id = 'test-vdur-id-2'
747 vdur
.vim_id
= 'test-vim-id-2'
748 vdur
.vm_flavor
.vcpu_count
= 4
749 vdur
.vm_flavor
.memory_mb
= 100
750 vdur
.vm_flavor
.storage_gb
= 2
752 class MockPlugin(object):
754 self
._metrics
= dict()
755 self
._metrics
['test-vim-id-1'] = RwmonYang
.NfviMetrics()
756 self
._metrics
['test-vim-id-2'] = RwmonYang
.NfviMetrics()
758 def nfvi_metrics(self
, account
, vim_id
):
759 metrics
= self
._metrics
[vim_id
]
761 if vim_id
== 'test-vim-id-1':
762 metrics
.memory
.used
+= 1000
764 metrics
.memory
.used
+= 2000
768 class MockFactory(PluginFactory
):
771 def create(self
, cloud_account
):
772 plugin
= rw_peas
.PeasPlugin("rwmon_mock", 'RwMon-1.0')
773 impl
= plugin
.get_interface("Monitoring")
774 impl
.set_impl(MockPlugin())
777 # Modify the mock plugin factory
778 self
.monitor
._nfvi
_plugins
._factories
["mock"] = MockFactory()
780 # Add the cloud account the monitor
781 self
.monitor
.add_cloud_account(self
.account
)
783 # Add the VNFR to the monitor.
784 self
.monitor
.add_vnfr(vnfr
)
786 @wait_for_pending_tasks(self
.loop
)
789 # call #1 (time = 0.00s)
790 # The metrics for these VDURs have not been populated yet so a
791 # default metrics object (all zeros) is returned, and a request is
792 # scheduled with the data source to retrieve the metrics.
793 metrics1
= self
.monitor
.retrieve_nfvi_metrics('test-vdur-id-1')
794 metrics2
= self
.monitor
.retrieve_nfvi_metrics('test-vdur-id-2')
796 self
.assertEqual(0, metrics1
.memory
.used
)
797 self
.assertEqual(0, metrics2
.memory
.used
)
799 self
.loop
.run_until_complete(call1())
801 @wait_for_pending_tasks(self
.loop
)
804 # call #2 (wait 0.05s)
805 # The metrics have been populated with data from the data source
806 # due to the request made during call #1.
807 yield from asyncio
.sleep(0.05)
809 metrics1
= self
.monitor
.retrieve_nfvi_metrics('test-vdur-id-1')
810 metrics2
= self
.monitor
.retrieve_nfvi_metrics('test-vdur-id-2')
812 self
.assertEqual(1000, metrics1
.memory
.used
)
813 self
.assertEqual(2000, metrics2
.memory
.used
)
815 self
.loop
.run_until_complete(call2())
817 @wait_for_pending_tasks(self
.loop
)
820 # call #3 (wait 0.50s)
821 # This call exceeds 0.1s (the sample interval of the plugin)
822 # from when the data was retrieved. The cached metrics are
823 # immediately returned, but a request is made to the data source to
824 # refresh these metrics.
825 yield from asyncio
.sleep(0.10)
827 metrics1
= self
.monitor
.retrieve_nfvi_metrics('test-vdur-id-1')
828 metrics2
= self
.monitor
.retrieve_nfvi_metrics('test-vdur-id-2')
830 self
.assertEqual(1000, metrics1
.memory
.used
)
831 self
.assertEqual(2000, metrics2
.memory
.used
)
833 self
.loop
.run_until_complete(call3())
835 @wait_for_pending_tasks(self
.loop
)
838 # call #4 (wait 1.00s)
839 # The metrics retrieved differ from those in call #3 because the
840 # cached metrics have been updated.
841 yield from asyncio
.sleep(0.10)
842 metrics1
= self
.monitor
.retrieve_nfvi_metrics('test-vdur-id-1')
843 metrics2
= self
.monitor
.retrieve_nfvi_metrics('test-vdur-id-2')
845 self
.assertEqual(2000, metrics1
.memory
.used
)
846 self
.assertEqual(4000, metrics2
.memory
.used
)
848 self
.loop
.run_until_complete(call4())
851 def main(argv
=sys
.argv
[1:]):
852 logging
.basicConfig(format
='TEST %(message)s')
854 parser
= argparse
.ArgumentParser()
855 parser
.add_argument('-v', '--verbose', action
='store_true')
857 args
= parser
.parse_args(argv
)
859 # Set the global logging level
860 logging
.getLogger().setLevel(logging
.DEBUG
if args
.verbose
else logging
.ERROR
)
862 # Set the logger in this test to use a null handler
863 logging
.getLogger('test-logger').addHandler(logging
.NullHandler())
865 # The unittest framework requires a program name, so use the name of this
866 # file instead (we do not want to have to pass a fake program name to main
867 # when this is called from the interpreter).
868 unittest
.main(argv
=[__file__
] + argv
,
869 testRunner
=xmlrunner
.XMLTestRunner(
870 output
=os
.environ
["RIFT_MODULE_TEST"]))
872 if __name__
== '__main__':