3 # Copyright (c) 2017 All rights reserved
4 # This program and the accompanying materials
5 # are made available under the terms of the Apache License, Version 2.0
6 # which accompanies this distribution, and is available at
8 # http://www.apache.org/licenses/LICENSE-2.0
17 import functest.utils.functest_logger as ft_logger
18 import functest.utils.openstack_utils as os_utils
19 from opnfv.deployment.factory import Factory as DeploymentFactory
21 from sdnvpn.lib import config as sdnvpn_config
23 logger = ft_logger.Logger("sndvpn_test_utils").getLogger()
25 common_config = sdnvpn_config.CommonConfig()
31 def create_net(neutron_client, name):
32 logger.debug("Creating network %s", name)
33 net_id = os_utils.create_neutron_net(neutron_client, name)
36 "There has been a problem when creating the neutron network")
41 def create_subnet(neutron_client, name, cidr, net_id):
42 logger.debug("Creating subnet %s in network %s with cidr %s",
44 subnet_id = os_utils.create_neutron_subnet(neutron_client,
50 "There has been a problem when creating the neutron subnet")
55 def create_network(neutron_client, net, subnet1, cidr1,
56 router, subnet2=None, cidr2=None):
57 """Network assoc won't work for networks/subnets created by this function.
59 It is an ODL limitation due to it handling routers as vpns.
60 See https://bugs.opendaylight.org/show_bug.cgi?id=6962"""
61 network_dic = os_utils.create_network_full(neutron_client,
68 "There has been a problem when creating the neutron network")
70 net_id = network_dic["net_id"]
71 subnet_id = network_dic["subnet_id"]
72 router_id = network_dic["router_id"]
74 if subnet2 is not None:
75 logger.debug("Creating and attaching a second subnet...")
76 subnet_id = os_utils.create_neutron_subnet(
77 neutron_client, subnet2, cidr2, net_id)
80 "There has been a problem when creating the second subnet")
82 logger.debug("Subnet '%s' created successfully" % subnet_id)
83 return net_id, subnet_id, router_id
86 def create_instance(nova_client,
98 if 'flavor' not in kwargs:
99 kwargs['flavor'] = common_config.default_flavor
101 logger.info("Creating instance '%s'..." % name)
103 "Configuration:\n name=%s \n flavor=%s \n image=%s \n"
104 " network=%s\n secgroup=%s \n hypervisor=%s \n"
105 " fixed_ip=%s\n files=%s\n userdata=\n%s\n"
106 % (name, kwargs['flavor'], image_id, network_id, sg_id,
107 compute_node, fixed_ip, files, userdata))
108 instance = os_utils.create_instance_and_wait_for_active(
115 av_zone=compute_node,
120 logger.error("Error while booting instance.")
123 logger.debug("Instance '%s' booted successfully. IP='%s'." %
124 (name, instance.networks.itervalues().next()[0]))
125 # Retrieve IP of INSTANCE
126 # instance_ip = instance.networks.get(network_id)[0]
129 logger.debug("Adding '%s' to security group '%s'..."
130 % (name, secgroup_name))
132 logger.debug("Adding '%s' to security group '%s'..."
134 os_utils.add_secgroup_to_instance(nova_client, instance.id, sg_id)
139 def generate_ping_userdata(ips_array):
142 ips = ("%s %s" % (ips, ip))
144 ips = ips.replace(' ', ' ')
145 return ("#!/bin/sh\n"
150 " ping -c 1 $ip 2>&1 >/dev/null\n"
152 " if [ \"Z$RES\" = \"Z0\" ] ; then\n"
153 " echo ping $ip OK\n"
154 " else echo ping $ip KO\n"
162 def generate_userdata_common():
163 return ("#!/bin/sh\n"
164 "sudo mkdir -p /home/cirros/.ssh/\n"
165 "sudo chown cirros:cirros /home/cirros/.ssh/\n"
166 "sudo chown cirros:cirros /home/cirros/id_rsa\n"
167 "mv /home/cirros/id_rsa /home/cirros/.ssh/\n"
168 "sudo echo ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAAAgnWtSS98Am516e"
169 "stBsq0jbyOB4eLMUYDdgzsUHsnxFQCtACwwAg9/2uq3FoGUBUWeHZNsT6jcK9"
170 "sCMEYiS479CUCzbrxcd8XaIlK38HECcDVglgBNwNzX/WDfMejXpKzZG61s98rU"
171 "ElNvZ0YDqhaqZGqxIV4ejalqLjYrQkoly3R+2k= "
172 "cirros@test1>/home/cirros/.ssh/authorized_keys\n"
173 "sudo chown cirros:cirros /home/cirros/.ssh/authorized_keys\n"
174 "chmod 700 /home/cirros/.ssh\n"
175 "chmod 644 /home/cirros/.ssh/authorized_keys\n"
176 "chmod 600 /home/cirros/.ssh/id_rsa\n"
180 def generate_userdata_with_ssh(ips_array):
181 u1 = generate_userdata_common()
185 ips = ("%s %s" % (ips, ip))
187 ips = ips.replace(' ', ' ')
193 " hostname=$(ssh -y -i /home/cirros/.ssh/id_rsa "
194 "cirros@$ip 'hostname' </dev/zero 2>/dev/null)\n"
196 " if [ \"Z$RES\" = \"Z0\" ]; then echo $ip $hostname;\n"
197 " else echo $ip 'not reachable';fi;\n"
205 def get_installerHandler():
206 installer_type = str(os.environ['INSTALLER_TYPE'].lower())
207 installer_ip = get_installer_ip()
209 if installer_type not in ["fuel", "apex"]:
210 raise ValueError("%s is not supported" % installer_type)
212 if installer_type in ["apex"]:
213 developHandler = DeploymentFactory.get_handler(
217 pkey_file="/root/.ssh/id_rsa")
219 if installer_type in ["fuel"]:
220 developHandler = DeploymentFactory.get_handler(
225 return developHandler
229 developHandler = get_installerHandler()
230 return developHandler.get_nodes()
233 def get_installer_ip():
234 return str(os.environ['INSTALLER_IP'])
237 def get_instance_ip(instance):
238 instance_ip = instance.networks.itervalues().next()[0]
242 def wait_for_instance(instance):
243 logger.info("Waiting for instance %s to get a DHCP lease..." % instance.id)
244 # The sleep this function replaced waited for 80s
247 pattern = "Lease of .* obtained, lease time"
248 expected_regex = re.compile(pattern)
250 while tries > 0 and not expected_regex.search(console_log):
251 console_log = instance.get_console_output()
252 time.sleep(sleep_time)
255 if not expected_regex.search(console_log):
256 logger.error("Instance %s seems to have failed leasing an IP."
262 def wait_for_instances_up(*args):
263 check = [wait_for_instance(instance) for instance in args]
267 def wait_for_bgp_net_assoc(neutron_client, bgpvpn_id, net_id):
271 logger.debug("Waiting for network %s to associate with BGPVPN %s "
272 % (bgpvpn_id, net_id))
274 while tries > 0 and net_id not in nets:
275 nets = os_utils.get_bgpvpn_networks(neutron_client, bgpvpn_id)
276 time.sleep(sleep_time)
278 if net_id not in nets:
279 logger.error("Association of network %s with BGPVPN %s failed" %
285 def wait_for_bgp_net_assocs(neutron_client, bgpvpn_id, *args):
286 check = [wait_for_bgp_net_assoc(neutron_client, bgpvpn_id, id)
288 # Return True if all associations succeeded
292 def wait_for_bgp_router_assoc(neutron_client, bgpvpn_id, router_id):
296 logger.debug("Waiting for router %s to associate with BGPVPN %s "
297 % (bgpvpn_id, router_id))
298 while tries > 0 and router_id not in routers:
299 routers = os_utils.get_bgpvpn_routers(neutron_client, bgpvpn_id)
300 time.sleep(sleep_time)
302 if router_id not in routers:
303 logger.error("Association of router %s with BGPVPN %s failed" %
304 (router_id, bgpvpn_id))
309 def wait_for_bgp_router_assocs(neutron_client, bgpvpn_id, *args):
310 check = [wait_for_bgp_router_assoc(neutron_client, bgpvpn_id, id)
312 # Return True if all associations succeeded
316 def wait_before_subtest(*args, **kwargs):
317 ''' This is a placeholder.
318 TODO: Replace delay with polling logic. '''
322 def assert_and_get_compute_nodes(nova_client, required_node_number=2):
323 """Get the compute nodes in the deployment
325 Exit if the deployment doesn't have enough compute nodes"""
326 compute_nodes = os_utils.get_hypervisors(nova_client)
328 num_compute_nodes = len(compute_nodes)
329 if num_compute_nodes < 2:
330 logger.error("There are %s compute nodes in the deployment. "
331 "Minimum number of nodes to complete the test is 2."
335 logger.debug("Compute nodes: %s" % compute_nodes)
339 def open_icmp_ssh(neutron_client, security_group_id):
340 os_utils.create_secgroup_rule(neutron_client,
344 os_utils.create_secgroup_rule(neutron_client,
350 def open_bgp_port(neutron_client, security_group_id):
351 os_utils.create_secgroup_rule(neutron_client,
357 def exec_cmd(cmd, verbose):
359 logger.debug("Executing '%s'" % cmd)
360 p = subprocess.Popen(cmd, shell=True, stdout=subprocess.PIPE,
361 stderr=subprocess.STDOUT)
363 for line in iter(p.stdout.readline, b''):
370 returncode = p.wait()
372 logger.error("Command %s failed to execute." % cmd)
375 return output, success
378 def check_odl_fib(ip, controller_ip):
379 """Check that there is an entry in the ODL Fib for `ip`"""
380 url = "http://" + controller_ip + \
381 ":8181/restconf/config/odl-fib:fibEntries/"
382 logger.debug("Querring '%s' for FIB entries", url)
383 res = requests.get(url, auth=(ODL_USER, ODL_PASS))
384 if res.status_code != 200:
385 logger.error("OpenDaylight response status code: %s", res.status_code)
387 logger.debug("Checking whether '%s' is in the OpenDaylight FIB"
389 logger.debug("OpenDaylight FIB: \n%s" % res.text)
390 return ip in res.text
393 def run_odl_cmd(odl_node, cmd):
394 '''Run a command in the OpenDaylight Karaf shell
396 This is a bit flimsy because of shell quote escaping, make sure that
397 the cmd passed does not have any top level double quotes or this
400 The /dev/null is used because client works, but outputs something
401 that contains "ERROR" and run_cmd doesn't like that.
404 karaf_cmd = '/opt/opendaylight/bin/client "%s" 2>/dev/null' % cmd
405 return odl_node.run_cmd(karaf_cmd)
408 def wait_for_cloud_init(instance):
410 # ubuntu images take a long time to start
414 instance_log = instance.get_console_output()
415 if "Failed to run module" in instance_log:
417 logger.error("Cloud init failed to run. Reason: %s",
420 if re.search(r"Cloud-init v. .+ finished at", instance_log):
423 time.sleep(sleep_time)
427 logger.error("Cloud init timed out"