Minor change in virtual env creation
[csit.git] / resources / libraries / python / SetupFramework.py
1 # Copyright (c) 2016 Cisco and/or its affiliates.
2 # Licensed under the Apache License, Version 2.0 (the "License");
3 # you may not use this file except in compliance with the License.
4 # You may obtain a copy of the License at:
5 #
6 #     http://www.apache.org/licenses/LICENSE-2.0
7 #
8 # Unless required by applicable law or agreed to in writing, software
9 # distributed under the License is distributed on an "AS IS" BASIS,
10 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
11 # See the License for the specific language governing permissions and
12 # limitations under the License.
13
14 import shlex
15 from subprocess import Popen, PIPE, call
16 from multiprocessing import Pool
17 from tempfile import NamedTemporaryFile
18 from os.path import basename
19 from robot.api import logger
20 from robot.libraries.BuiltIn import BuiltIn
21 from ssh import SSH
22 from constants import Constants as con
23 from topology import NodeType
24
25 __all__ = ["SetupFramework"]
26
27
28 def pack_framework_dir():
29     """Pack the testing WS into temp file, return its name."""
30
31     tmpfile = NamedTemporaryFile(suffix=".tgz", prefix="openvpp-testing-")
32     file_name = tmpfile.name
33     tmpfile.close()
34
35     proc = Popen(
36         shlex.split("tar --exclude-vcs -zcf {0} .".format(file_name)),
37         stdout=PIPE, stderr=PIPE)
38     (stdout, stderr) = proc.communicate()
39
40     logger.debug(stdout)
41     logger.debug(stderr)
42
43     return_code = proc.wait()
44     if 0 != return_code:
45         raise Exception("Could not pack testing framework.")
46
47     return file_name
48
49
50 def copy_tarball_to_node(tarball, node):
51     logger.console('Copying tarball to {0}'.format(node['host']))
52     ssh = SSH()
53     ssh.connect(node)
54
55     ssh.scp(tarball, "/tmp/")
56
57
58 def extract_tarball_at_node(tarball, node):
59     logger.console('Extracting tarball to {0} on {1}'.format(
60         con.REMOTE_FW_DIR, node['host']))
61     ssh = SSH()
62     ssh.connect(node)
63
64     cmd = 'sudo rm -rf {1}; mkdir {1} ; tar -zxf {0} -C {1}; ' \
65         'rm -f {0}'.format(tarball, con.REMOTE_FW_DIR)
66     (ret_code, stdout, stderr) = ssh.exec_command(cmd, timeout=30)
67     if 0 != ret_code:
68         logger.error('Unpack error: {0}'.format(stderr))
69         raise Exception('Failed to unpack {0} at node {1}'.format(
70             tarball, node['host']))
71
72
73 def create_env_directory_at_node(node):
74     """Create fresh virtualenv to a directory, install pip requirements."""
75     logger.console('Extracting virtualenv, installing requirements.txt '
76                    'on {0}'.format(node['host']))
77     ssh = SSH()
78     ssh.connect(node)
79     (ret_code, stdout, stderr) = ssh.exec_command(
80             'cd {0} && rm -rf env && virtualenv env && '
81             '. env/bin/activate && '
82             'pip install -r requirements.txt'.format(con.REMOTE_FW_DIR), timeout=100)
83     if 0 != ret_code:
84         logger.error('Virtualenv creation error: {0}'.format(stdout + stderr))
85         raise Exception('Virtualenv setup failed')
86     else:
87         logger.console('Virtualenv created on {0}'.format(node['host']))
88
89 def install_dependencies(node):
90     """TEMPORARY FUNCTION TO INSTALL DEPENDENCIES ON NODES BEFORE THE VIRL
91     HOSTS HAVE ALL PREREQUISITES INSTALLED"""
92     logger.console('Installing prerequisites on {0}'.format(node['host']))
93     ssh = SSH()
94     ssh.connect(node)
95     (ret_code, stdout, stderr) = ssh.exec_command(
96             'sudo apt-get -y update; ' \
97             'sudo apt-get -y install python-virtualenv python-dev', timeout=120)
98     if 0 != ret_code:
99         logger.error('Failed to install prerequisites: {0}'.
100                 format(stdout + stderr))
101         raise Exception('Virtualenv setup failed')
102
103 def setup_node(args):
104     tarball, remote_tarball, node = args
105     copy_tarball_to_node(tarball, node)
106     extract_tarball_at_node(remote_tarball, node)
107     install_dependencies(node)
108     if node['type'] == NodeType.TG:
109         create_env_directory_at_node(node)
110
111
112 def delete_local_tarball(tarball):
113     call(shlex.split('sh -c "rm {0} > /dev/null 2>&1"'.format(tarball)))
114
115
116 class SetupFramework(object):
117     """Setup suite run on topology nodes.
118
119     Many VAT/CLI based tests need the scripts at remote hosts before executing
120     them. This class packs the whole testing directory and copies it over
121     to all nodes in topology under /tmp/
122     """
123
124     def __init__(self):
125         pass
126
127     def setup_framework(self, nodes):
128         """Pack the whole directory and extract in temp on each node."""
129
130         tarball = pack_framework_dir()
131         msg = 'Framework packed to {0}'.format(tarball)
132         logger.console(msg)
133         logger.trace(msg)
134         remote_tarball = "/tmp/{0}".format(basename(tarball))
135
136         # Turn off loggining since we use multiprocessing
137         log_level = BuiltIn().set_log_level('NONE')
138         params = ((tarball, remote_tarball, node) for node in nodes.values())
139         pool = Pool(processes=len(nodes))
140         result = pool.map_async(setup_node, params)
141         pool.close()
142         pool.join()
143
144         logger.info(
145             'Executed node setups in parallel, waiting for processes to end')
146         result.wait()
147
148         logger.info('Results: {0}'.format(result.get()))
149
150         # Turn on loggining
151         BuiltIn().set_log_level(log_level)
152         logger.trace('Test framework copied to all topology nodes')
153         delete_local_tarball(tarball)