Merge "Remove shell Healtcheck test case"
[functest.git] / functest / opnfv_tests / vnf / ims / orchestra_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 socket
12 import sys
13 import time
14 import yaml
15
16 import functest.core.vnf_base as vnf_base
17 import functest.utils.functest_logger as ft_logger
18 import functest.utils.functest_utils as ft_utils
19 import functest.utils.openstack_utils as os_utils
20 import os
21 from functest.utils.constants import CONST
22
23 from org.openbaton.cli.agents.agents import MainAgent
24 from org.openbaton.cli.errors.errors import NfvoException
25
26
27 def servertest(host, port):
28     args = socket.getaddrinfo(host, port, socket.AF_INET, socket.SOCK_STREAM)
29     for family, socktype, proto, canonname, sockaddr in args:
30         s = socket.socket(family, socktype, proto)
31         try:
32             s.connect(sockaddr)
33         except socket.error:
34             return False
35         else:
36             s.close()
37             return True
38
39
40 class ImsVnf(vnf_base.VnfOnBoardingBase):
41     def __init__(self, project='functest', case='orchestra_ims',
42                  repo='', cmd=''):
43         super(ImsVnf, self).__init__(project, case, repo, cmd)
44         self.ob_password = "openbaton"
45         self.ob_username = "admin"
46         self.ob_https = False
47         self.ob_port = "8080"
48         self.ob_ip = "localhost"
49         self.ob_instance_id = ""
50         self.logger = ft_logger.Logger("orchestra_ims").getLogger()
51         self.case_dir = os.path.join(CONST.dir_functest_test, 'vnf/ims/')
52         self.data_dir = CONST.dir_ims_data
53         self.test_dir = CONST.dir_repo_vims_test
54         self.ob_projectid = ""
55         self.keystone_client = os_utils.get_keystone_client()
56         self.ob_nsr_id = ""
57         self.main_agent = None
58         # vIMS Data directory creation
59         if not os.path.exists(self.data_dir):
60             os.makedirs(self.data_dir)
61         # Retrieve the configuration
62         try:
63             self.config = CONST.__getattribute__(
64                 'vnf_{}_config'.format(self.case_name))
65         except:
66             raise Exception("Orchestra VNF config file not found")
67         config_file = self.case_dir + self.config
68         self.imagename = get_config("openbaton.imagename", config_file)
69         self.market_link = get_config("openbaton.marketplace_link",
70                                       config_file)
71         self.images = get_config("tenant_images", config_file)
72
73     def deploy_orchestrator(self, **kwargs):
74         self.logger.info("Additional pre-configuration steps")
75         nova_client = os_utils.get_nova_client()
76         neutron_client = os_utils.get_neutron_client()
77         glance_client = os_utils.get_glance_client()
78
79         # Import images if needed
80         self.logger.info("Upload some OS images if it doesn't exist")
81         temp_dir = os.path.join(self.data_dir, "tmp/")
82         for image_name, image_url in self.images.iteritems():
83             self.logger.info("image: %s, url: %s" % (image_name, image_url))
84             try:
85                 image_id = os_utils.get_image_id(glance_client,
86                                                  image_name)
87                 self.logger.info("image_id: %s" % image_id)
88             except:
89                 self.logger.error("Unexpected error: %s" % sys.exc_info()[0])
90
91             if image_id == '':
92                 self.logger.info("""%s image doesn't exist on glance repository. Try
93                 downloading this image and upload on glance !""" % image_name)
94                 image_id = download_and_add_image_on_glance(glance_client,
95                                                             image_name,
96                                                             image_url,
97                                                             temp_dir)
98             if image_id == '':
99                 self.step_failure(
100                     "Failed to find or upload required OS "
101                     "image for this deployment")
102         network_dic = os_utils.create_network_full(neutron_client,
103                                                    "openbaton_mgmt",
104                                                    "openbaton_mgmt_subnet",
105                                                    "openbaton_router",
106                                                    "192.168.100.0/24")
107
108         # orchestrator VM flavor
109         self.logger.info("Check medium Flavor is available, if not create one")
110         flavor_exist, flavor_id = os_utils.get_or_create_flavor(
111             "m1.medium",
112             "4096",
113             '20',
114             '2',
115             public=True)
116         self.logger.debug("Flavor id: %s" % flavor_id)
117
118         if not network_dic:
119             self.logger.error("There has been a problem when creating the "
120                               "neutron network")
121
122         network_id = network_dic["net_id"]
123
124         self.logger.info("Creating floating IP for VM in advance...")
125         floatip_dic = os_utils.create_floating_ip(neutron_client)
126         floatip = floatip_dic['fip_addr']
127
128         if floatip is None:
129             self.logger.error("Cannot create floating IP.")
130
131         userdata = "#!/bin/bash\n"
132         userdata += "set -x\n"
133         userdata += "set -e\n"
134         userdata += "echo \"nameserver   8.8.8.8\" >> /etc/resolv.conf\n"
135         userdata += "apt-get install curl\n"
136         userdata += ("echo \"rabbitmq_broker_ip=%s\" > ./config_file\n"
137                      % floatip)
138         userdata += "echo \"mysql=no\" >> ./config_file\n"
139         userdata += ("echo \"ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQCuPXrV3"
140                      "geeHc6QUdyUr/1Z+yQiqLcOskiEGBiXr4z76MK4abiFmDZ18OMQlc"
141                      "fl0p3kS0WynVgyaOHwZkgy/DIoIplONVr2CKBKHtPK+Qcme2PVnCtv"
142                      "EqItl/FcD+1h5XSQGoa+A1TSGgCod/DPo+pes0piLVXP8Ph6QS1k7S"
143                      "ic7JDeRQ4oT1bXYpJ2eWBDMfxIWKZqcZRiGPgMIbJ1iEkxbpeaAd9O"
144                      "4MiM9nGCPESmed+p54uYFjwEDlAJZShcAZziiZYAvMZhvAhe6USljc"
145                      "7YAdalAnyD/jwCHuwIrUw/lxo7UdNCmaUxeobEYyyFA1YVXzpNFZya"
146                      "XPGAAYIJwEq/ openbaton@opnfv\" >> /home/ubuntu/.ssh/aut"
147                      "horized_keys\n")
148         userdata += "cat ./config_file\n"
149         userdata += ("curl -s http://get.openbaton.org/bootstrap "
150                      "> ./bootstrap\n")
151         userdata += "export OPENBATON_COMPONENT_AUTOSTART=false\n"
152         bootstrap = "sh ./bootstrap release -configFile=./config_file"
153         userdata += bootstrap + "\n"
154
155         userdata += ("echo \"nfvo.plugin.timeout=300000\" >> "
156                      "/etc/openbaton/openbaton-nfvo.properties\n")
157         userdata += "service openbaton-nfvo restart\n"
158         userdata += "service openbaton-vnfm-generic restart\n"
159
160         sg_id = os_utils.create_security_group_full(neutron_client,
161                                                     "orchestra-sec-group",
162                                                     "allowall")
163
164         os_utils.create_secgroup_rule(neutron_client, sg_id, "ingress",
165                                       "icmp", 0, 255)
166         os_utils.create_secgroup_rule(neutron_client, sg_id, "egress",
167                                       "icmp", 0, 255)
168         os_utils.create_secgroup_rule(neutron_client, sg_id, "ingress",
169                                       "tcp", 1, 65535)
170         os_utils.create_secgroup_rule(neutron_client, sg_id, "ingress",
171                                       "udp", 1, 65535)
172         os_utils.create_secgroup_rule(neutron_client, sg_id, "egress",
173                                       "tcp", 1, 65535)
174         os_utils.create_secgroup_rule(neutron_client, sg_id, "egress",
175                                       "udp", 1, 65535)
176
177         self.logger.info("Security group set")
178
179         self.logger.info("Create instance....")
180         self.logger.info("flavor: m1.medium\n"
181                          "image: %s\n"
182                          "network_id: %s\n"
183                          "userdata: %s\n"
184                          % (self.imagename, network_id, userdata))
185
186         instance = os_utils.create_instance_and_wait_for_active(
187             "m1.medium",
188             os_utils.get_image_id(glance_client, self.imagename),
189             network_id,
190             "orchestra-openbaton",
191             config_drive=False,
192             userdata=userdata)
193
194         self.ob_instance_id = instance.id
195
196         self.logger.info("Adding sec group to orchestra instance")
197         os_utils.add_secgroup_to_instance(nova_client,
198                                           self.ob_instance_id, sg_id)
199
200         self.logger.info("Associating floating ip: '%s' to VM '%s' "
201                          % (floatip, "orchestra-openbaton"))
202         if not os_utils.add_floating_ip(nova_client, instance.id, floatip):
203             self.logger.error("Cannot associate floating IP to VM.")
204             self.step_failure("Cannot associate floating IP to VM.")
205
206         self.logger.info("Waiting for nfvo to be up and running...")
207         x = 0
208         while x < 100:
209             if servertest(floatip, "8080"):
210                 break
211             else:
212                 self.logger.debug("openbaton is not started yet")
213                 time.sleep(5)
214                 x += 1
215
216         if x == 100:
217             self.logger.error("Openbaton is not started correctly")
218             self.step_failure("Openbaton is not started correctly")
219
220         self.ob_ip = floatip
221         self.ob_password = "openbaton"
222         self.ob_username = "admin"
223         self.ob_https = False
224         self.ob_port = "8080"
225
226         self.logger.info("Deploy orchestrator: OK")
227
228     def deploy_vnf(self):
229         self.logger.info("vIMS Deployment")
230
231         self.main_agent = MainAgent(nfvo_ip=self.ob_ip,
232                                     nfvo_port=self.ob_port,
233                                     https=self.ob_https,
234                                     version=1,
235                                     username=self.ob_username,
236                                     password=self.ob_password)
237
238         project_agent = self.main_agent.get_agent("project", self.ob_projectid)
239         for p in json.loads(project_agent.find()):
240             if p.get("name") == "default":
241                 self.ob_projectid = p.get("id")
242                 break
243
244         self.logger.debug("project id: %s" % self.ob_projectid)
245         if self.ob_projectid == "":
246             self.logger.error("Default project id was not found!")
247             self.step_failure("Default project id was not found!")
248
249         vim_json = {
250             "name": "vim-instance",
251             "authUrl": os_utils.get_credentials().get("auth_url"),
252             "tenant": os_utils.get_credentials().get("tenant_name"),
253             "username": os_utils.get_credentials().get("username"),
254             "password": os_utils.get_credentials().get("password"),
255             "keyPair": "opnfv",
256             # TODO change the keypair to correct value
257             # or upload a correct one or remove it
258             "securityGroups": [
259                 "default",
260                 "orchestra-sec-group"
261             ],
262             "type": "openstack",
263             "location": {
264                 "name": "opnfv",
265                 "latitude": "52.525876",
266                 "longitude": "13.314400"
267             }
268         }
269
270         self.logger.debug("vim: %s" % vim_json)
271
272         self.main_agent.get_agent(
273             "vim",
274             project_id=self.ob_projectid).create(entity=json.dumps(vim_json))
275
276         market_agent = self.main_agent.get_agent("market",
277                                                  project_id=self.ob_projectid)
278
279         nsd = {}
280         try:
281             self.logger.info("sending: %s" % self.market_link)
282             nsd = market_agent.create(entity=self.market_link)
283             self.logger.info("Onboarded nsd: " + nsd.get("name"))
284         except NfvoException as e:
285             self.step_failure(e.message)
286
287         nsr_agent = self.main_agent.get_agent("nsr",
288                                               project_id=self.ob_projectid)
289         nsd_id = nsd.get('id')
290         if nsd_id is None:
291             self.step_failure("NSD not onboarded correctly")
292
293         nsr = None
294         try:
295             nsr = nsr_agent.create(nsd_id)
296         except NfvoException as e:
297             self.step_failure(e.message)
298
299         if nsr is None:
300             self.step_failure("NSR not deployed correctly")
301
302         i = 0
303         self.logger.info("waiting NSR to go to active...")
304         while nsr.get("status") != 'ACTIVE':
305             i += 1
306             if i == 100:
307                 self.step_failure("After %s sec the nsr did not go to active.."
308                                   % 5 * 100)
309             time.sleep(5)
310             nsr = json.loads(nsr_agent.find(nsr.get('id')))
311
312         deploy_vnf = {'status': "PASS", 'result': nsr}
313         self.ob_nsr_id = nsr.get("id")
314         self.logger.info("Deploy VNF: OK")
315         return deploy_vnf
316
317     def test_vnf(self):
318         # Adaptations probably needed
319         # code used for cloudify_ims
320         # ruby client on jumphost calling the vIMS on the SUT
321         return
322
323     def clean(self):
324         self.main_agent.get_agent(
325             "nsr",
326             project_id=self.ob_projectid).delete(self.ob_nsr_id)
327         time.sleep(5)
328         os_utils.delete_instance(nova_client=os_utils.get_nova_client(),
329                                  instance_id=self.ob_instance_id)
330         # TODO question is the clean removing also the VM?
331         # I think so since is goinf to remove the tenant...
332         super(ImsVnf, self).clean()
333
334     def main(self, **kwargs):
335         self.logger.info("Orchestra IMS VNF onboarding test starting")
336         self.execute()
337         self.logger.info("Orchestra IMS VNF onboarding test executed")
338         if self.criteria is "PASS":
339             return self.EX_OK
340         else:
341             return self.EX_RUN_ERROR
342
343     def run(self):
344         kwargs = {}
345         return self.main(**kwargs)
346
347
348 if __name__ == '__main__':
349     test = ImsVnf()
350     test.deploy_orchestrator()
351     test.deploy_vnf()
352     test.clean()
353
354
355 # ----------------------------------------------------------
356 #
357 #               UTILS
358 #
359 # -----------------------------------------------------------
360 def get_config(parameter, file):
361     """
362     Returns the value of a given parameter in file.yaml
363     parameter must be given in string format with dots
364     Example: general.openstack.image_name
365     """
366     with open(file) as f:
367         file_yaml = yaml.safe_load(f)
368     f.close()
369     value = file_yaml
370     for element in parameter.split("."):
371         value = value.get(element)
372         if value is None:
373             raise ValueError("The parameter %s is not defined in"
374                              " reporting.yaml" % parameter)
375     return value
376
377
378 def download_and_add_image_on_glance(glance, image_name,
379                                      image_url, data_dir):
380     dest_path = data_dir
381     if not os.path.exists(dest_path):
382         os.makedirs(dest_path)
383     file_name = image_url.rsplit('/')[-1]
384     if not ft_utils.download_url(image_url, dest_path):
385         return False
386     image = os_utils.create_glance_image(
387         glance, image_name, dest_path + file_name)
388     if not image:
389         return False
390     return image