Merge "Limit the substitution of ' in env vars"
[functest.git] / functest / opnfv_tests / vnf / ims / cloudify_ims.py
1 #!/usr/bin/env python
2
3 # Copyright (c) 2016 Orange and others.
4 #
5 # All rights reserved. This program and the accompanying materials
6 # are made available under the terms of the Apache License, Version 2.0
7 # which accompanies this distribution, and is available at
8 # http://www.apache.org/licenses/LICENSE-2.0
9
10 import json
11 import os
12 import requests
13 import subprocess
14 import sys
15 import time
16 import yaml
17
18 import functest.core.vnf_base as vnf_base
19 import functest.utils.functest_logger as ft_logger
20 import functest.utils.functest_utils as ft_utils
21 import functest.utils.openstack_utils as os_utils
22
23 from clearwater import Clearwater
24 from functest.utils.constants import CONST
25 from orchestrator_cloudify import Orchestrator
26
27
28 class ImsVnf(vnf_base.VnfOnBoardingBase):
29
30     def __init__(self, project='functest', case='cloudify_ims',
31                  repo='', cmd=''):
32         super(ImsVnf, self).__init__(project, case, repo, cmd)
33         self.logger = ft_logger.Logger("vIMS").getLogger()
34         self.case_dir = os.path.join(CONST.dir_functest_test, 'vnf/ims/')
35         self.data_dir = CONST.dir_ims_data
36         self.test_dir = CONST.dir_repo_vims_test
37
38         # Retrieve the configuration
39         try:
40             self.config = CONST.__getattribute__(
41                 'vnf_{}_config'.format(self.case_name))
42         except:
43             raise Exception("VNF config file not found")
44
45         config_file = self.case_dir + self.config
46         self.orchestrator = dict(
47             requirements=get_config("cloudify.requirements", config_file),
48             blueprint=get_config("cloudify.blueprint", config_file),
49             inputs=get_config("cloudify.inputs", config_file)
50         )
51         self.logger.debug("Orchestrator configuration: %s" % self.orchestrator)
52         self.vnf = dict(
53             blueprint=get_config("clearwater.blueprint", config_file),
54             deployment_name=get_config("clearwater.deployment_name",
55                                        config_file),
56             inputs=get_config("clearwater.inputs", config_file),
57             requirements=get_config("clearwater.requirements", config_file)
58         )
59         self.logger.debug("VNF configuration: %s" % self.vnf)
60
61         self.images = get_config("tenant_images", config_file)
62         self.logger.info("Images needed for vIMS: %s" % self.images)
63
64         # vIMS Data directory creation
65         if not os.path.exists(self.data_dir):
66             os.makedirs(self.data_dir)
67
68     def deploy_orchestrator(self, **kwargs):
69
70         self.logger.info("Additional pre-configuration steps")
71         self.neutron_client = os_utils.get_neutron_client(self.creds)
72         self.glance_client = os_utils.get_glance_client(self.creds)
73         self.keystone_client = os_utils.get_keystone_client(self.creds)
74         self.nova_client = os_utils.get_nova_client(self.creds)
75
76         # needs some images
77         self.logger.info("Upload some OS images if it doesn't exist")
78         temp_dir = os.path.join(self.data_dir, "tmp/")
79         for image_name, image_url in self.images.iteritems():
80             self.logger.info("image: %s, url: %s" % (image_name, image_url))
81             try:
82                 image_id = os_utils.get_image_id(self.glance_client,
83                                                  image_name)
84                 self.logger.debug("image_id: %s" % image_id)
85             except:
86                 self.logger.error("Unexpected error: %s" % sys.exc_info()[0])
87
88             if image_id == '':
89                 self.logger.info("""%s image doesn't exist on glance repository. Try
90                 downloading this image and upload on glance !""" % image_name)
91                 image_id = download_and_add_image_on_glance(self.glance_client,
92                                                             image_name,
93                                                             image_url,
94                                                             temp_dir)
95             if image_id == '':
96                 self.step_failure(
97                     "Failed to find or upload required OS "
98                     "image for this deployment")
99         # Need to extend quota
100         self.logger.info("Update security group quota for this tenant")
101         tenant_id = os_utils.get_tenant_id(self.keystone_client,
102                                            self.tenant_name)
103         self.logger.debug("Tenant id found %s" % tenant_id)
104         if not os_utils.update_sg_quota(self.neutron_client,
105                                         tenant_id, 50, 100):
106             self.step_failure("Failed to update security group quota" +
107                               " for tenant " + self.tenant_name)
108         self.logger.debug("group quota extended")
109
110         # start the deployment of cloudify
111         public_auth_url = os_utils.get_endpoint('identity')
112
113         self.logger.debug("CFY inputs: %s" % self.orchestrator['inputs'])
114         cfy = Orchestrator(self.data_dir, self.orchestrator['inputs'])
115         self.orchestrator['object'] = cfy
116         self.logger.debug("Orchestrator object created")
117
118         self.logger.debug("Tenant name: %s" % self.tenant_name)
119
120         cfy.set_credentials(username=self.tenant_name,
121                             password=self.tenant_name,
122                             tenant_name=self.tenant_name,
123                             auth_url=public_auth_url)
124         self.logger.info("Credentials set in CFY")
125
126         # orchestrator VM flavor
127         self.logger.info("Check Flavor is available, if not create one")
128         self.logger.debug("Flavor details %s " %
129                           self.orchestrator['requirements']['ram_min'])
130         flavor_exist, flavor_id = os_utils.get_or_create_flavor(
131             "m1.large",
132             self.orchestrator['requirements']['ram_min'],
133             '1',
134             '1',
135             public=True)
136         self.logger.debug("Flavor id: %s" % flavor_id)
137
138         if not flavor_id:
139             self.logger.info("Available flavors are: ")
140             self.logger.info(self.nova_client.flavor.list())
141             self.step_failure("Failed to find required flavor"
142                               "for this deployment")
143         cfy.set_flavor_id(flavor_id)
144         self.logger.debug("Flavor OK")
145
146         # orchestrator VM image
147         self.logger.debug("Orchestrator image")
148         if 'os_image' in self.orchestrator['requirements'].keys():
149             image_id = os_utils.get_image_id(
150                 self.glance_client,
151                 self.orchestrator['requirements']['os_image'])
152             self.logger.debug("Orchestrator image id: %s" % image_id)
153             if image_id == '':
154                 self.logger.error("CFY image not found")
155                 self.step_failure("Failed to find required OS image"
156                                   " for cloudify manager")
157         else:
158             self.step_failure("Failed to find required OS image"
159                               " for cloudify manager")
160
161         cfy.set_image_id(image_id)
162         self.logger.debug("Orchestrator image set")
163
164         self.logger.debug("Get External network")
165         ext_net = os_utils.get_external_net(self.neutron_client)
166         self.logger.debug("External network: %s" % ext_net)
167         if not ext_net:
168             self.step_failure("Failed to get external network")
169
170         cfy.set_external_network_name(ext_net)
171         self.logger.debug("CFY External network set")
172
173         self.logger.debug("get resolvconf")
174         ns = ft_utils.get_resolvconf_ns()
175         if ns:
176             cfy.set_nameservers(ns)
177             self.logger.debug("Resolvconf set")
178
179         if 'compute' in self.nova_client.client.services_url:
180             cfy.set_nova_url(self.nova_client.client.services_url['compute'])
181         if self.neutron_client.httpclient.endpoint_url is not None:
182             cfy.set_neutron_url(self.neutron_client.httpclient.endpoint_url)
183
184         self.logger.info("Prepare virtualenv for cloudify-cli")
185         cmd = "chmod +x " + self.case_dir + "create_venv.sh"
186         ft_utils.execute_command(cmd)
187         time.sleep(3)
188         cmd = self.case_dir + "create_venv.sh " + self.data_dir
189         ft_utils.execute_command(cmd)
190
191         cfy.download_manager_blueprint(
192             self.orchestrator['blueprint']['url'],
193             self.orchestrator['blueprint']['branch'])
194
195         cfy.deploy_manager()
196         return {'status': 'PASS', 'result': ''}
197
198     def deploy_vnf(self):
199         cw = Clearwater(self.vnf.inputs, self.orchestrator.object, self.logger)
200         self.vnf.object = cw
201
202         self.logger.info("Collect flavor id for all clearwater vm")
203         flavor_exist, flavor_id = os_utils.get_or_create_flavor(
204             "m1.small",
205             self.vnf['requirements']['ram_min'],
206             '1',
207             '1',
208             public=True)
209         self.logger.debug("Flavor id: %s" % flavor_id)
210         if not flavor_id:
211             self.logger.info("Available flavors are: ")
212             self.logger.info(self.nova_client.flavor.list())
213             self.step_failure("Failed to find required flavor"
214                               " for this deployment")
215
216         cw.set_flavor_id(flavor_id)
217
218         # VMs image
219         if 'os_image' in self.vnf.requirements.keys():
220             image_id = os_utils.get_image_id(
221                 self.glance_client, self.vnf['requirements']['os_image'])
222             if image_id == '':
223                 self.step_failure("Failed to find required OS image"
224                                   " for clearwater VMs")
225         else:
226             self.step_failure("Failed to find required OS image"
227                               " for clearwater VMs")
228
229         cw.set_image_id(image_id)
230
231         ext_net = os_utils.get_external_net(self.neutron_client)
232         if not ext_net:
233             self.step_failure("Failed to get external network")
234
235         cw.set_external_network_name(ext_net)
236
237         cw.deploy_vnf()
238         return {'status': 'PASS', 'result': ''}
239
240     def test_vnf(self):
241         script = "source {0}venv_cloudify/bin/activate; "
242         script += "cd {0}; "
243         script += "cfy status | grep -Eo \"([0-9]{{1,3}}\.){{3}}[0-9]{{1,3}}\""
244         cmd = "/bin/bash -c '" + script.format(self.data_dir) + "'"
245
246         try:
247             self.logger.debug("Trying to get clearwater manager IP ... ")
248             mgr_ip = os.popen(cmd).read()
249             mgr_ip = mgr_ip.splitlines()[0]
250         except:
251             self.step_failure("Unable to retrieve the IP of the "
252                               "cloudify manager server !")
253
254         api_url = "http://" + mgr_ip + "/api/v2"
255         dep_outputs = requests.get(api_url + "/deployments/" +
256                                    self.vnf.deployment_name + "/outputs")
257         dns_ip = dep_outputs.json()['outputs']['dns_ip']
258         ellis_ip = dep_outputs.json()['outputs']['ellis_ip']
259
260         ellis_url = "http://" + ellis_ip + "/"
261         url = ellis_url + "accounts"
262
263         params = {"password": "functest",
264                   "full_name": "opnfv functest user",
265                   "email": "functest@opnfv.fr",
266                   "signup_code": "secret"}
267
268         rq = requests.post(url, data=params)
269         i = 20
270         while rq.status_code != 201 and i > 0:
271             rq = requests.post(url, data=params)
272             i = i - 1
273             time.sleep(10)
274
275         if rq.status_code == 201:
276             url = ellis_url + "session"
277             rq = requests.post(url, data=params)
278             cookies = rq.cookies
279
280         url = ellis_url + "accounts/" + params['email'] + "/numbers"
281         if cookies != "":
282             rq = requests.post(url, cookies=cookies)
283             i = 24
284             while rq.status_code != 200 and i > 0:
285                 rq = requests.post(url, cookies=cookies)
286                 i = i - 1
287                 time.sleep(25)
288
289         if rq.status_code != 200:
290             self.step_failure("Unable to create a number: %s"
291                               % rq.json()['reason'])
292
293         nameservers = ft_utils.get_resolvconf_ns()
294         resolvconf = ""
295         for ns in nameservers:
296             resolvconf += "\nnameserver " + ns
297
298         if dns_ip != "":
299             script = ('echo -e "nameserver ' + dns_ip + resolvconf +
300                       '" > /etc/resolv.conf; ')
301             script += 'source /etc/profile.d/rvm.sh; '
302             script += 'cd {0}; '
303             script += ('rake test[{1}] SIGNUP_CODE="secret"')
304
305             cmd = ("/bin/bash -c '" +
306                    script.format(self.data_dir, self.inputs["public_domain"]) +
307                    "'")
308             output_file = "output.txt"
309             f = open(output_file, 'w+')
310             subprocess.call(cmd, shell=True, stdout=f,
311                             stderr=subprocess.STDOUT)
312             f.close()
313
314             f = open(output_file, 'r')
315             result = f.read()
316             if result != "":
317                 self.logger.debug(result)
318
319             vims_test_result = ""
320             tempFile = os.path.join(self.test_dir, "temp.json")
321             try:
322                 self.logger.debug("Trying to load test results")
323                 with open(tempFile) as f:
324                     vims_test_result = json.load(f)
325                 f.close()
326             except:
327                 self.logger.error("Unable to retrieve test results")
328
329             try:
330                 os.remove(tempFile)
331             except:
332                 self.logger.error("Deleting file failed")
333
334             if vims_test_result != '':
335                 return {'status': 'PASS', 'result': vims_test_result}
336             else:
337                 return {'status': 'FAIL', 'result': ''}
338
339     def clean(self):
340         self.vnf.object.undeploy_vnf()
341         self.orchestrator.object.undeploy_manager()
342         super(ImsVnf, self).clean()
343
344     def main(self, **kwargs):
345         self.logger.info("Cloudify IMS VNF onboarding test starting")
346         self.execute()
347         self.logger.info("Cloudify IMS VNF onboarding test executed")
348         if self.criteria is "PASS":
349             return self.EX_OK
350         else:
351             return self.EX_RUN_ERROR
352
353     def run(self):
354         kwargs = {}
355         return self.main(**kwargs)
356
357
358 # ----------------------------------------------------------
359 #
360 #               YAML UTILS
361 #
362 # -----------------------------------------------------------
363 def get_config(parameter, file):
364     """
365     Returns the value of a given parameter in file.yaml
366     parameter must be given in string format with dots
367     Example: general.openstack.image_name
368     """
369     with open(file) as f:
370         file_yaml = yaml.safe_load(f)
371     f.close()
372     value = file_yaml
373     for element in parameter.split("."):
374         value = value.get(element)
375         if value is None:
376             raise ValueError("The parameter %s is not defined in"
377                              " reporting.yaml" % parameter)
378     return value
379
380
381 def download_and_add_image_on_glance(glance, image_name, image_url, data_dir):
382     dest_path = data_dir
383     if not os.path.exists(dest_path):
384         os.makedirs(dest_path)
385     file_name = image_url.rsplit('/')[-1]
386     if not ft_utils.download_url(image_url, dest_path):
387         return False
388
389     image = os_utils.create_glance_image(
390         glance, image_name, dest_path + file_name)
391     if not image:
392         return False
393
394     return image