584d780a5a53c973041c632405ab0421c2742f31
[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.admin_creds)
72         self.glance_client = os_utils.get_glance_client(self.admin_creds)
73         self.keystone_client = os_utils.get_keystone_client(self.admin_creds)
74         self.nova_client = os_utils.get_nova_client(self.admin_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             '50',
134             '2',
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         self.logger.info("Prepare virtualenv for cloudify-cli")
180         cmd = "chmod +x " + self.case_dir + "create_venv.sh"
181         ft_utils.execute_command(cmd)
182         time.sleep(3)
183         cmd = self.case_dir + "create_venv.sh " + self.data_dir
184         ft_utils.execute_command(cmd)
185
186         cfy.download_manager_blueprint(
187             self.orchestrator['blueprint']['url'],
188             self.orchestrator['blueprint']['branch'])
189
190         error = cfy.deploy_manager()
191         if error:
192             self.logger.error(error)
193             return {'status': 'FAIL', 'result': error}
194         else:
195             return {'status': 'PASS', 'result': ''}
196
197     def deploy_vnf(self):
198         cw = Clearwater(self.vnf.inputs, self.orchestrator.object, self.logger)
199         self.vnf.object = cw
200
201         self.logger.info("Collect flavor id for all clearwater vm")
202         flavor_exist, flavor_id = os_utils.get_or_create_flavor(
203             "m1.small",
204             self.vnf['requirements']['ram_min'],
205             '20',
206             '1',
207             public=True)
208         self.logger.debug("Flavor id: %s" % flavor_id)
209         if not flavor_id:
210             self.logger.info("Available flavors are: ")
211             self.logger.info(self.nova_client.flavor.list())
212             self.step_failure("Failed to find required flavor"
213                               " for this deployment")
214
215         cw.set_flavor_id(flavor_id)
216
217         # VMs image
218         if 'os_image' in self.vnf.requirements.keys():
219             image_id = os_utils.get_image_id(
220                 self.glance_client, self.vnf['requirements']['os_image'])
221             if image_id == '':
222                 self.step_failure("Failed to find required OS image"
223                                   " for clearwater VMs")
224         else:
225             self.step_failure("Failed to find required OS image"
226                               " for clearwater VMs")
227
228         cw.set_image_id(image_id)
229
230         ext_net = os_utils.get_external_net(self.neutron_client)
231         if not ext_net:
232             self.step_failure("Failed to get external network")
233
234         cw.set_external_network_name(ext_net)
235
236         error = cw.deploy_vnf()
237         if error:
238             self.logger.error(error)
239             return {'status': 'FAIL', 'result': error}
240         else:
241             return {'status': 'PASS', 'result': ''}
242
243     def test_vnf(self):
244         script = "source {0}venv_cloudify/bin/activate; "
245         script += "cd {0}; "
246         script += "cfy status | grep -Eo \"([0-9]{{1,3}}\.){{3}}[0-9]{{1,3}}\""
247         cmd = "/bin/bash -c '" + script.format(self.data_dir) + "'"
248
249         try:
250             self.logger.debug("Trying to get clearwater manager IP ... ")
251             mgr_ip = os.popen(cmd).read()
252             mgr_ip = mgr_ip.splitlines()[0]
253         except:
254             self.step_failure("Unable to retrieve the IP of the "
255                               "cloudify manager server !")
256
257         api_url = "http://" + mgr_ip + "/api/v2"
258         dep_outputs = requests.get(api_url + "/deployments/" +
259                                    self.vnf.deployment_name + "/outputs")
260         dns_ip = dep_outputs.json()['outputs']['dns_ip']
261         ellis_ip = dep_outputs.json()['outputs']['ellis_ip']
262
263         ellis_url = "http://" + ellis_ip + "/"
264         url = ellis_url + "accounts"
265
266         params = {"password": "functest",
267                   "full_name": "opnfv functest user",
268                   "email": "functest@opnfv.fr",
269                   "signup_code": "secret"}
270
271         rq = requests.post(url, data=params)
272         i = 20
273         while rq.status_code != 201 and i > 0:
274             rq = requests.post(url, data=params)
275             i = i - 1
276             time.sleep(10)
277
278         if rq.status_code == 201:
279             url = ellis_url + "session"
280             rq = requests.post(url, data=params)
281             cookies = rq.cookies
282
283         url = ellis_url + "accounts/" + params['email'] + "/numbers"
284         if cookies != "":
285             rq = requests.post(url, cookies=cookies)
286             i = 24
287             while rq.status_code != 200 and i > 0:
288                 rq = requests.post(url, cookies=cookies)
289                 i = i - 1
290                 time.sleep(25)
291
292         if rq.status_code != 200:
293             self.step_failure("Unable to create a number: %s"
294                               % rq.json()['reason'])
295
296         nameservers = ft_utils.get_resolvconf_ns()
297         resolvconf = ""
298         for ns in nameservers:
299             resolvconf += "\nnameserver " + ns
300
301         if dns_ip != "":
302             script = ('echo -e "nameserver ' + dns_ip + resolvconf +
303                       '" > /etc/resolv.conf; ')
304             script += 'source /etc/profile.d/rvm.sh; '
305             script += 'cd {0}; '
306             script += ('rake test[{1}] SIGNUP_CODE="secret"')
307
308             cmd = ("/bin/bash -c '" +
309                    script.format(self.data_dir, self.inputs["public_domain"]) +
310                    "'")
311             output_file = "output.txt"
312             f = open(output_file, 'w+')
313             subprocess.call(cmd, shell=True, stdout=f,
314                             stderr=subprocess.STDOUT)
315             f.close()
316
317             f = open(output_file, 'r')
318             result = f.read()
319             if result != "":
320                 self.logger.debug(result)
321
322             vims_test_result = ""
323             tempFile = os.path.join(self.test_dir, "temp.json")
324             try:
325                 self.logger.debug("Trying to load test results")
326                 with open(tempFile) as f:
327                     vims_test_result = json.load(f)
328                 f.close()
329             except:
330                 self.logger.error("Unable to retrieve test results")
331
332             try:
333                 os.remove(tempFile)
334             except:
335                 self.logger.error("Deleting file failed")
336
337             if vims_test_result != '':
338                 return {'status': 'PASS', 'result': vims_test_result}
339             else:
340                 return {'status': 'FAIL', 'result': ''}
341
342     def clean(self):
343         self.vnf.object.undeploy_vnf()
344         self.orchestrator.object.undeploy_manager()
345         super(ImsVnf, self).clean()
346
347     def main(self, **kwargs):
348         self.logger.info("Cloudify IMS VNF onboarding test starting")
349         self.execute()
350         self.logger.info("Cloudify IMS VNF onboarding test executed")
351         if self.criteria is "PASS":
352             return self.EX_OK
353         else:
354             return self.EX_RUN_ERROR
355
356     def run(self):
357         kwargs = {}
358         return self.main(**kwargs)
359
360
361 # ----------------------------------------------------------
362 #
363 #               YAML UTILS
364 #
365 # -----------------------------------------------------------
366 def get_config(parameter, file):
367     """
368     Returns the value of a given parameter in file.yaml
369     parameter must be given in string format with dots
370     Example: general.openstack.image_name
371     """
372     with open(file) as f:
373         file_yaml = yaml.safe_load(f)
374     f.close()
375     value = file_yaml
376     for element in parameter.split("."):
377         value = value.get(element)
378         if value is None:
379             raise ValueError("The parameter %s is not defined in"
380                              " reporting.yaml" % parameter)
381     return value
382
383
384 def download_and_add_image_on_glance(glance, image_name, image_url, data_dir):
385     dest_path = data_dir
386     if not os.path.exists(dest_path):
387         os.makedirs(dest_path)
388     file_name = image_url.rsplit('/')[-1]
389     if not ft_utils.download_url(image_url, dest_path):
390         return False
391
392     image = os_utils.create_glance_image(
393         glance, image_name, dest_path + file_name)
394     if not image:
395         return False
396
397     return image