3 # Copyright (c) 2018 Orange and others.
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
10 """Ease deploying a single VM reachable via ssh
12 It offers a simple way to create all tenant network ressources + a VM for
13 advanced testcases (e.g. deploying an orchestrator).
22 import os_client_config
25 from xtesting.core import testcase
27 from functest.core import tenantnetwork
28 from functest.utils import config
31 class SingleVm1(tenantnetwork.TenantNetwork1):
32 """Deploy a single VM reachable via ssh (scenario1)
34 It inherits from TenantNetwork1 which creates all network resources and
35 completes it by booting a VM attached to that network.
37 It ensures that all testcases inheriting from SingleVm1 could work
38 without specific configurations (or at least read the same config data).
40 # pylint: disable=too-many-instance-attributes
42 __logger = logging.getLogger(__name__)
43 filename = '/home/opnfv/functest/images/cirros-0.4.0-x86_64-disk.img'
48 ssh_connect_timeout = 60
50 def __init__(self, **kwargs):
51 if "case_name" not in kwargs:
52 kwargs["case_name"] = 'singlevm1'
53 super(SingleVm1, self).__init__(**kwargs)
54 self.orig_cloud = self.cloud
61 self.ssh = paramiko.SSHClient()
62 (_, self.key_filename) = tempfile.mkstemp()
64 def _publish_image(self):
67 config.CONF, '{}_extra_properties'.format(self.case_name), None)
68 self.image = self.cloud.create_image(
69 '{}-img_{}'.format(self.case_name, self.guid),
71 config.CONF, '{}_image'.format(self.case_name),
74 self.__logger.debug("image: %s", self.image)
76 def create_sg_rules(self):
77 """Create the security group
79 It can be overriden to set other rules according to the services
82 Raises: Exception on error
85 self.sec = self.cloud.create_security_group(
86 '{}-sg_{}'.format(self.case_name, self.guid),
87 'created by OPNFV Functest ({})'.format(self.case_name))
88 self.cloud.create_security_group_rule(
89 self.sec.id, port_range_min='22', port_range_max='22',
90 protocol='tcp', direction='ingress')
91 self.cloud.create_security_group_rule(
92 self.sec.id, protocol='icmp', direction='ingress')
95 assert self.orig_cloud
97 self.flavor = self.orig_cloud.create_flavor(
98 '{}-flavor_{}'.format(self.case_name, self.guid),
99 getattr(config.CONF, '{}_flavor_ram'.format(self.case_name),
101 getattr(config.CONF, '{}_flavor_vcpus'.format(self.case_name),
103 getattr(config.CONF, '{}_flavor_disk'.format(self.case_name),
105 self.__logger.debug("flavor: %s", self.flavor)
106 self.cloud.set_flavor_specs(
107 self.flavor.id, getattr(config.CONF, 'flavor_extra_specs', {}))
109 self.keypair = self.cloud.create_keypair(
110 '{}-kp_{}'.format(self.case_name, self.guid))
111 self.__logger.debug("keypair: %s", self.keypair)
112 self.__logger.debug("private_key: %s", self.keypair.private_key)
113 with open(self.key_filename, 'w') as private_key_file:
114 private_key_file.write(self.keypair.private_key)
116 self.sshvm = self.cloud.create_server(
117 '{}-vm_{}'.format(self.case_name, self.guid),
118 image=self.image.id, flavor=self.flavor.id,
119 key_name=self.keypair.id,
120 auto_ip=False, wait=True,
121 network=self.network.id,
122 security_groups=[self.sec.id])
123 self.__logger.debug("vm: %s", self.sshvm)
124 self.fip = self.cloud.create_floating_ip(
125 network=self.ext_net.id, server=self.sshvm)
126 self.__logger.debug("floating_ip: %s", self.fip)
127 self.sshvm = self.cloud.wait_for_server(self.sshvm, auto_ip=False)
131 p_console = self.cloud.get_server_console(self.sshvm.id)
132 self.__logger.debug("vm console: \n%s", p_console)
133 self.ssh.set_missing_host_key_policy(paramiko.client.AutoAddPolicy())
134 for loop in range(6):
137 self.sshvm.public_v4,
140 '{}_image_user'.format(self.case_name), self.username),
141 key_filename=self.key_filename,
144 '{}_vm_ssh_connect_timeout'.format(self.case_name),
145 self.ssh_connect_timeout))
147 except Exception: # pylint: disable=broad-except
149 "try %s: cannot connect to %s", loop + 1,
150 self.sshvm.public_v4)
153 self.__logger.error("cannot connect to %s", self.sshvm.public_v4)
158 """Say hello world via ssh
160 It can be overriden to execute any command.
162 Returns: echo exit codes
164 (_, stdout, _) = self.ssh.exec_command('echo Hello World')
165 self.__logger.info("output:\n%s", stdout.read())
166 return stdout.channel.recv_exit_status()
168 def run(self, **kwargs):
171 Here are the main actions:
175 - create the security group
176 - execute the right command over ssh
180 - TestCase.EX_RUN_ERROR on error
182 status = testcase.TestCase.EX_RUN_ERROR
185 super(SingleVm1, self).run(**kwargs)
187 self._publish_image()
188 self.create_sg_rules()
190 assert self._connect()
191 if not self.execute():
193 status = testcase.TestCase.EX_OK
194 except Exception: # pylint: disable=broad-except
195 self.__logger.exception('Cannot run %s', self.case_name)
197 self.stop_time = time.time()
202 assert self.orig_cloud
204 self.cloud.delete_server(self.sshvm, wait=True)
205 self.cloud.delete_security_group(self.sec.id)
206 self.cloud.delete_image(self.image)
207 self.orig_cloud.delete_flavor(self.flavor.id)
208 self.cloud.delete_keypair(self.keypair.id)
209 self.cloud.delete_floating_ip(self.fip.id)
210 self.cloud.delete_image(self.image)
211 except Exception: # pylint: disable=broad-except
215 class SingleVm2(SingleVm1):
216 """Deploy a single VM reachable via ssh (scenario2)
218 It creates new user/project before creating and configuring all tenant
219 network ressources and vms required by advanced testcases.
221 It ensures that all testcases inheriting from SingleVm2 could work
222 without specific configurations (or at least read the same config data).
225 __logger = logging.getLogger(__name__)
227 def __init__(self, **kwargs):
228 if "case_name" not in kwargs:
229 kwargs["case_name"] = 'singlevm2'
230 super(SingleVm2, self).__init__(**kwargs)
233 self.domain = self.cloud.get_domain(
234 name_or_id=self.cloud.auth.get(
235 "project_domain_name", "Default"))
236 except Exception: # pylint: disable=broad-except
238 self.__logger.exception("Cannot connect to Cloud")
241 self.orig_cloud = None
242 self.password = str(uuid.uuid4())
244 def run(self, **kwargs):
248 self.project = self.cloud.create_project(
249 name='{}-project_{}'.format(self.case_name, self.guid),
250 description="Created by OPNFV Functest: {}".format(
252 domain_id=self.domain.id)
253 self.__logger.debug("project: %s", self.project)
254 self.user = self.cloud.create_user(
255 name='{}-user_{}'.format(self.case_name, self.guid),
256 password=self.password,
257 default_project=self.project.id,
258 domain_id=self.domain.id)
259 self.__logger.debug("user: %s", self.user)
260 self.orig_cloud = self.cloud
261 os.environ["OS_USERNAME"] = self.user.name
262 os.environ["OS_PROJECT_NAME"] = self.user.default_project_id
263 cloud_config = os_client_config.get_config()
264 self.cloud = shade.OpenStackCloud(cloud_config=cloud_config)
265 os.environ["OS_USERNAME"] = self.orig_cloud.auth["username"]
266 os.environ["OS_PROJECT_NAME"] = self.orig_cloud.auth[
268 except Exception: # pylint: disable=broad-except
269 self.__logger.exception("Cannot create user or project")
270 return testcase.TestCase.EX_RUN_ERROR
271 return super(SingleVm2, self).run(**kwargs)
276 assert self.orig_cloud
277 super(SingleVm2, self).clean()
279 assert self.project.id
280 self.orig_cloud.delete_user(self.user.id)
281 self.orig_cloud.delete_project(self.project.id)
282 except Exception: # pylint: disable=broad-except
283 self.__logger.exception("cannot clean all ressources")