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