Add functional test for reservation support

Added functional test for reservation support.

Implements: blueprint reservation-vnfm
Change-Id: Ia09240875bd5c0ceb70602d73a0a5c94a4dde060
This commit is contained in:
niraj singh 2019-02-22 13:46:00 +00:00
parent 2595cc112f
commit 1d7cd6f604
7 changed files with 375 additions and 15 deletions

View File

@ -108,6 +108,7 @@ pyparsing==2.2.0
pyperclip==1.6.0
pyroute2==0.4.21
python-barbicanclient==4.5.2
python-blazarclient==1.0.1
python-dateutil==2.7.2
python-editor==1.0.3
python-glanceclient==2.9.1

View File

@ -20,6 +20,7 @@ VNF_CIRROS_DEAD_TIMEOUT = 500
ACTIVE_SLEEP_TIME = 3
DEAD_SLEEP_TIME = 1
SCALE_WINDOW_SLEEP_TIME = 120
SCALE_SLEEP_TIME = 120
NS_CREATE_TIMEOUT = 400
NS_DELETE_TIMEOUT = 300
NOVA_CLIENT_VERSION = 2
@ -29,3 +30,9 @@ VDU_AUTOHEALING_TIMEOUT = 500
VDU_AUTOHEALING_SLEEP_TIME = 3
VNF_CIRROS_PENDING_HEAL_TIMEOUT = 300
PENDING_SLEEP_TIME = 3
# Blazar related
LEASE_EVENT_STATUS = 'DONE'
START_LEASE_EVET_TYPE = 'start_lease'
LEASE_CHECK_EVENT_TIMEOUT = 300
LEASE_CHECK_SLEEP_TIME = 3

View File

@ -0,0 +1,90 @@
tosca_definitions_version: tosca_simple_profile_for_nfv_1_0_0
description: VNF TOSCA template with flavor input parameters
metadata:
template_name: sample-tosca-vnfd-instance-reservation
topology_template:
node_templates:
VDU1:
type: tosca.nodes.nfv.VDU.Tacker
properties:
image: cirros-0.4.0-x86_64-disk
flavor: { get_input: flavor }
reservation_metadata:
resource_type: {get_input: resource_type}
id: { get_input: server_group_id }
CP1:
type: tosca.nodes.nfv.CP.Tacker
properties:
management: true
order: 0
anti_spoofing_protection: false
requirements:
- virtualLink:
node: VL1
- virtualBinding:
node: VDU1
CP2:
type: tosca.nodes.nfv.CP.Tacker
properties:
order: 1
anti_spoofing_protection: false
requirements:
- virtualLink:
node: VL2
- virtualBinding:
node: VDU1
CP3:
type: tosca.nodes.nfv.CP.Tacker
properties:
order: 2
anti_spoofing_protection: false
requirements:
- virtualLink:
node: VL3
- virtualBinding:
node: VDU1
VL1:
type: tosca.nodes.nfv.VL
properties:
network_name: net_mgmt
vendor: Tacker
VL2:
type: tosca.nodes.nfv.VL
properties:
network_name: net0
vendor: Tacker
VL3:
type: tosca.nodes.nfv.VL
properties:
network_name: net1
vendor: Tacker
policies:
- RSV:
type: tosca.policies.tacker.Reservation
reservation:
start_actions: [SP_RSV]
before_end_actions: [SP_RSV]
end_actions: [noop]
properties:
lease_id: { get_input: lease_id }
- SP_RSV:
type: tosca.policies.tacker.Scaling
properties:
increment: 2
cooldown: 60
min_instances: 0
max_instances: 2
default_instances: 0
targets: [VDU1]

View File

@ -15,6 +15,7 @@
import time
import yaml
from blazarclient import client as blazar_client
from glanceclient.v2 import client as glance_client
from keystoneauth1.identity import v3
from keystoneauth1 import session
@ -106,6 +107,21 @@ class BaseTackerTest(base.BaseTestCase):
data['project_domain_name'] = domain_name
return clients.OpenstackClients(auth_attr=data).heat
def blazarclient(cls):
data = cls.get_credentials()
domain_name = data.pop('domain_name')
data['user_domain_name'] = domain_name
data['project_domain_name'] = domain_name
auth_ses = clients.OpenstackClients(auth_attr=data).keystone_session
args = {
'session': auth_ses,
'service_type': 'reservation',
'interface': 'public',
'region_name': 'RegionOne',
}
client = blazar_client.Client(**args)
return client
@classmethod
def glanceclient(cls):
vim_params = cls.get_credentials()
@ -252,3 +268,14 @@ class BaseTackerTest(base.BaseTestCase):
resource_name=resource_name)
resource_dict = resource_details.to_dict()
self.assertTrue(resource_dict['attributes']['port_security_enabled'])
def trigger_vnf(self, vnf, policy_name, policy_action):
credential = 'g0jtsxu9'
body = {"trigger": {'policy_name': policy_name,
'action_name': policy_action,
'params': {
'data': {'alarm_id': '35a80852-e24f-46ed-bd34-e2f831d00172', 'current': 'alarm'}, # noqa
'credential': credential}
}
}
self.client.post('/vnfs/%s/triggers' % vnf, body)

View File

@ -26,7 +26,6 @@ import yaml
class VnfTestAlarmMonitor(base.BaseTackerTest):
def _test_vnf_tosca_alarm(self, vnfd_file, vnf_name):
vnf_trigger_path = '/vnfs/%s/triggers'
input_yaml = read_file(vnfd_file)
tosca_dict = yaml.safe_load(input_yaml)
tosca_arg = {'vnfd': {'name': vnf_name,
@ -55,17 +54,6 @@ class VnfTestAlarmMonitor(base.BaseTackerTest):
self.assertEqual(count, len(jsonutils.loads(vnf[
'mgmt_ip_address'])['VDU1']))
def trigger_vnf(vnf, policy_name, policy_action):
credential = 'g0jtsxu9'
body = {"trigger": {'policy_name': policy_name,
'action_name': policy_action,
'params': {
'data': {'alarm_id': '35a80852-e24f-46ed-bd34-e2f831d00172', 'current': 'alarm'}, # noqa
'credential': credential}
}
}
self.client.post(vnf_trigger_path % vnf, body)
def _inject_monitoring_policy(vnfd_dict):
polices = vnfd_dict['topology_template'].get('policies', [])
mon_policy = dict()
@ -92,14 +80,15 @@ class VnfTestAlarmMonitor(base.BaseTackerTest):
vnf_id,
constants.VNF_CIRROS_CREATE_TIMEOUT,
constants.ACTIVE_SLEEP_TIME)
trigger_vnf(vnf_id, mon_policy_name, mon_policy_action)
self.trigger_vnf(vnf_id, mon_policy_name, mon_policy_action)
else:
if 'scaling_out' in mon_policy_name:
_waiting_time(2)
time.sleep(constants.SCALE_WINDOW_SLEEP_TIME)
# scaling-out backend action
scaling_out_action = mon_policy_action + '-out'
trigger_vnf(vnf_id, mon_policy_name, scaling_out_action)
self.trigger_vnf(
vnf_id, mon_policy_name, scaling_out_action)
_waiting_time(3)
@ -109,7 +98,8 @@ class VnfTestAlarmMonitor(base.BaseTackerTest):
time.sleep(constants.SCALE_WINDOW_SLEEP_TIME)
# scaling-in backend action
scaling_in_action = mon_policy_action + '-in'
trigger_vnf(vnf_id, scaling_in_name, scaling_in_action)
self.trigger_vnf(
vnf_id, scaling_in_name, scaling_in_action)
_waiting_time(2)

View File

@ -0,0 +1,244 @@
#
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
import ast
import time
import datetime
import json
import testtools
import yaml
from blazarclient import exception
from tacker.plugins.common import constants as evt_constants
from tacker.tests import constants
from tacker.tests.functional import base
from tacker.tests.utils import read_file
HYPERVISORS = []
def hypervisors():
global HYPERVISORS
client = base.BaseTackerTest.novaclient()
hypervisor_list = client.hypervisors.list()
for hypervisor in hypervisor_list:
if hypervisor.running_vms == 0:
HYPERVISORS.append(hypervisor)
return HYPERVISORS
class VnfTestReservationMonitor(base.BaseTackerTest):
def _test_vnf_tosca_reservation(self, vnfd_file, vnf_name,
lease_id, param_values=None):
input_yaml = read_file(vnfd_file)
tosca_dict = yaml.safe_load(input_yaml)
# TODO(niraj-singh): It's not possible to pass parameters through
# parameter file due to Bug #1799683. Once this bug is fixed, no need
# to update vnfd yaml.
vdu_prop = tosca_dict['topology_template']['node_templates']['VDU1']
vdu_prop['properties']['flavor'] = param_values.get('flavor')
vdu_prop['properties']['reservation_metadata']['id'] =\
param_values.get('server_group_id')
vdu_prop['properties']['reservation_metadata']['resource_type'] =\
param_values.get('resource_type')
policies = tosca_dict['topology_template']['policies']
policies[0]['RSV']['reservation']['properties']['lease_id'] =\
param_values.get('lease_id')
tosca_arg = {'vnfd': {'name': vnf_name,
'attributes': {'vnfd': tosca_dict}}}
blazarclient = self.blazarclient()
# Create vnfd with tosca template
vnfd_instance = self.client.create_vnfd(body=tosca_arg)
self.assertIsNotNone(vnfd_instance)
# Create vnf with vnfd_id
vnfd_id = vnfd_instance['vnfd']['id']
vnf_arg = {'vnf': {'vnfd_id': vnfd_id, 'name': vnf_name}}
vnf_instance = self.client.create_vnf(body=vnf_arg)
self.validate_vnf_instance(vnfd_instance, vnf_instance)
vnf_id = vnf_instance['vnf']['id']
def _get_reservation_policy(vnfd_dict):
policies = vnfd_dict['topology_template'].get('policies', [])
res_policy = dict()
for policy_dict in policies:
for name, policy in policy_dict.items():
if policy['type'] == constants.POLICY_RESERVATION:
reservations = policy['reservation']
for reserv_key, reserv_value in reservations.items():
if reserv_key == 'properties':
continue
for policy_action in reserv_value:
res_policy[reserv_key] = policy_action
return res_policy
def _check_lease_event_status():
lease_event_status = _verify_and_get_lease_event_status()
self.assertEqual(lease_event_status, constants.LEASE_EVENT_STATUS,
"Lease %(lease_id)s with status %(status)s is"
" expected to be %(target)s" %
{"lease_id": lease_id,
"status": lease_event_status,
"target": constants.LEASE_EVENT_STATUS})
def _verify_and_get_lease_event_status():
start_time = int(time.time())
while ((int(time.time()) - start_time) <
constants.LEASE_CHECK_EVENT_TIMEOUT):
lease_detail = blazarclient.lease.get(lease_id)
lease_events = lease_detail.get('events')
for event in lease_events:
lease_event_status = event.get('status')
if ((event.get('event_type') ==
constants.START_LEASE_EVET_TYPE) and (
lease_event_status ==
constants.LEASE_EVENT_STATUS)):
return lease_event_status
time.sleep(constants.LEASE_CHECK_SLEEP_TIME)
def _wait_vnf_active_and_assert_vdu_count(vdu_count, scale_type=None):
self.wait_until_vnf_active(
vnf_id,
constants.VNF_CIRROS_CREATE_TIMEOUT,
constants.ACTIVE_SLEEP_TIME)
vnf = self.client.show_vnf(vnf_id)['vnf']
# {"VDU1": ["10.0.0.14", "10.0.0.5"]}
if scale_type == 'scaling-in' and vdu_count == 0:
try:
# After sacling-in the vnf['mgmt_ip_address'] will be the
# list containg null values. As vnf['mgmt_ip_address']
# is string so we can not access ip address list for VDU1
# so converting that into the dict using ast lib.
# If the list contains the null value then it will raise
# ValueError so on the basis of that we are confirming the
# scaling-in is successful.
ast.literal_eval(vnf['mgmt_ip_address'])
self.fail("Scaling-in should not contain "
"mgmt_ip_address")
except ValueError:
assert True, ("Management Ip address list for VDU1"
"contains null values.")
elif scale_type == 'scaling-out':
self.assertEqual(vdu_count, len(json.loads(
vnf['mgmt_ip_address'])['VDU1']))
elif vdu_count == 0 and scale_type is None:
self.assertIsNone(vnf['mgmt_ip_address'])
reservation_policy = _get_reservation_policy(tosca_dict)
_wait_vnf_active_and_assert_vdu_count(0)
# trigger alarm for start action
start_action = reservation_policy.get('start_actions')
scaling_out_action = start_action + '-out'
_check_lease_event_status()
# scaling-out action
self.trigger_vnf(vnf_id, 'start_actions', scaling_out_action)
time.sleep(constants.SCALE_SLEEP_TIME)
# checking VDU's count after scaling out
_wait_vnf_active_and_assert_vdu_count(2, scale_type='scaling-out')
time.sleep(constants.SCALE_WINDOW_SLEEP_TIME)
# trigger alarm for before end action
before_end_action = reservation_policy.get('before_end_actions')
scaling_in_action = before_end_action + '-in'
# scaling-in action
self.trigger_vnf(vnf_id, 'before_end_actions', scaling_in_action)
time.sleep(constants.SCALE_SLEEP_TIME)
# checking VDU's count after scaling in
_wait_vnf_active_and_assert_vdu_count(0, scale_type='scaling-in')
self.verify_vnf_crud_events(
vnf_id, evt_constants.RES_EVT_SCALE,
evt_constants.ACTIVE, cnt=2)
self.verify_vnf_crud_events(
vnf_id, evt_constants.RES_EVT_SCALE,
evt_constants.PENDING_SCALE_OUT, cnt=1)
self.verify_vnf_crud_events(
vnf_id, evt_constants.RES_EVT_SCALE,
evt_constants.PENDING_SCALE_IN, cnt=1)
try:
self.client.delete_vnf(vnf_id)
except Exception:
assert False, (
"Failed to delete vnf %s after the reservation test" % vnf_id)
# Delete vnfd_instance
self.addCleanup(self.client.delete_vnfd, vnfd_id)
self.addCleanup(self.wait_until_vnf_delete, vnf_id,
constants.VNF_CIRROS_DELETE_TIMEOUT)
def _get_instance_reservation(self):
blazarclient = self.blazarclient()
reservations = [{'disk_gb': 0,
'vcpus': 1, 'memory_mb': 1,
'amount': 1, 'affinity': False,
'resource_properties': '',
'resource_type': 'virtual:instance'}]
events = []
start_date = (datetime.datetime.utcnow() + datetime.timedelta(
minutes=2)).strftime("%Y-%m-%d %H:%M")
end_date = (datetime.datetime.utcnow() + datetime.timedelta(
minutes=30)).strftime("%Y-%m-%d %H:%M")
host_added = False
for hypervisor in HYPERVISORS:
try:
blazar_host = blazarclient.host.create(
hypervisor.hypervisor_hostname)
host_added = True
except exception.BlazarClientException:
pass
if not host_added:
self.skipTest("Skip test as Blazar failed to create host from one"
" of available hypervisors '%s' as it found some"
" instances were running on them" %
",".join([hypervisor.hypervisor_hostname
for hypervisor in HYPERVISORS]))
instance_reservation = blazarclient.lease.create(
'test-reservation', start_date, end_date, reservations, events)
self.addCleanup(
blazarclient.host.delete, blazar_host.get('id'))
self.addCleanup(
blazarclient.lease.delete, instance_reservation['id'])
return instance_reservation, blazar_host
@testtools.skipIf(len(hypervisors()) == 0,
'Skip test as there are no'
' hypervisors available in nova')
def test_vnf_alarm_scale_with_instance_reservation(self):
instance_reservation, blazar_host = self._get_instance_reservation()
lease_id = str(instance_reservation['reservations'][0]['lease_id'])
flavor_id = str(instance_reservation['reservations'][0]['flavor_id'])
server_group_id = str(
instance_reservation['reservations'][0]['server_group_id'])
param_to_create_lease = {'lease_id': lease_id,
'flavor': flavor_id,
'server_group_id': server_group_id,
'resource_type': 'virtual_instance'}
self._test_vnf_tosca_reservation(
'sample-tosca-vnfd-instance-reservation.yaml',
'VNFD1', lease_id, param_to_create_lease)

View File

@ -18,3 +18,4 @@ tempest>=17.1.0 # Apache-2.0
testtools>=2.2.0 # MIT
WebTest>=2.0.27 # MIT
python-barbicanclient>=4.5.2 # Apache-2.0
python-blazarclient>=1.0.1 # Apache-2.0