blob: cb47805a518d47f5751109513bcd5477e37c7c8c [file] [log] [blame]
A R Karthickd0fdf3b2017-03-21 16:54:22 -07001import os
2import shutil
3import re
4from novaclient import client as nova_client
5from SSHTestAgent import SSHTestAgent
6from CordTestUtils import *
7
8class VSGAccess(object):
9
10 vcpe_map = {}
11 interface_map = {}
12 ip_addr_pattern = re.compile('[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}/[0-9]{1,2}$')
13
14 @classmethod
15 def setUp(cls):
16 try:
17 shutil.copy('/etc/resolv.conf', '/etc/resolv.conf.orig')
18 except:
19 pass
20
21 @classmethod
22 def tearDown(cls):
23 try:
24 shutil.copy('/etc/resolv.conf.orig', '/etc/resolv.conf')
25 except:
26 pass
27
28 @classmethod
29 def get_nova_credentials_v2(cls):
30 credential = {}
31 credential['username'] = os.environ['OS_USERNAME']
32 credential['api_key'] = os.environ['OS_PASSWORD']
33 credential['auth_url'] = os.environ['OS_AUTH_URL']
34 credential['project_id'] = os.environ['OS_TENANT_NAME']
35 return credential
36
37 @classmethod
38 def get_compute_nodes(cls):
39 credentials = cls.get_nova_credentials_v2()
40 nvclient = nova_client.Client('2', **credentials)
41 return nvclient.hypervisors.list()
42
43 @classmethod
44 def get_vsgs(cls, active = True):
45 credentials = cls.get_nova_credentials_v2()
46 nvclient = nova_client.Client('2', **credentials)
47 vsgs = nvclient.servers.list(search_opts = {'all_tenants': 1})
48 if active is True:
49 vsgs = filter(lambda vsg: vsg.status == 'ACTIVE', vsgs)
50 vsg_wrappers = []
51 for vsg in vsgs:
52 vsg_wrappers.append(VSGWrapper(vsg))
53 return vsg_wrappers
54
55 @classmethod
56 def open_mgmt(cls, intf = 'eth0'):
57 if intf in cls.interface_map:
58 gw = cls.interface_map[intf]['gw']
59 ip = cls.interface_map[intf]['ip']
60 if gw != '0.0.0.0':
61 current_gw, _ = get_default_gw()
62 cmds = [ 'route del default gw {}'.format(current_gw),
63 'ifconfig {} {} up'.format(intf, ip),
64 'route add default gw {}'.format(gw) ]
65 for cmd in cmds:
66 os.system(cmd)
67 shutil.copy('/etc/resolv.conf', '/etc/resolv.conf.lastdhcp')
68 shutil.copy('/etc/resolv.conf.orig', '/etc/resolv.conf')
69 return current_gw
70 return None
71
72 @classmethod
73 def close_mgmt(cls, restore_gw, intf = 'eth0'):
74 if restore_gw:
75 cmds = [ 'route del default gw 0.0.0.0',
76 'route add default gw {}'.format(restore_gw),
77 'cp /etc/resolv.conf.lastdhcp /etc/resolv.conf',
78 'rm -f /etc/resolv.conf.lastdhcp'
79 ]
80 for cmd in cmds:
81 os.system(cmd)
82
83 @classmethod
84 def health_check(cls):
85 '''Returns 0 if all active vsgs are reachable through the compute node'''
86 vsgs = cls.get_vsgs()
87 vsg_status = []
88 for vsg in vsgs:
89 vsg_status.append(vsg.get_health())
90 unreachable = filter(lambda st: st == False, vsg_status)
91 return len(unreachable) == 0
92
93 @classmethod
94 def get_vcpe_vsg(cls, vcpe):
95 '''Find the vsg hosting the vcpe service'''
96 if vcpe in cls.vcpe_map:
97 return cls.vcpe_map[vcpe]['vsg']
98 vsgs = cls.get_vsgs()
99 for vsg in vsgs:
100 cmd = 'sudo docker exec {} ls 2>/dev/null'.format(vcpe)
101 st, _ = vsg.run_cmd(cmd, timeout = 30)
102 if st == True:
103 return vsg
104 return None
105
106 @classmethod
107 def save_vcpe_config(cls, vsg, vcpe):
108 if vcpe not in cls.vcpe_map:
109 cmd_gw = "sudo docker exec %s ip route show | grep default | head -1 | awk '{print $3}'" %(vcpe)
110 vsg_ip = vsg.ip
111 if vsg_ip is None:
112 return False
113 st, output = vsg.run_cmd(cmd_gw, timeout = 30)
114 if st == False or not output:
115 return False
116 gw = output
117 cmd_wan = "sudo docker exec %s ip addr show eth0 |grep inet |head -1 | tr -s ' ' | awk '{print $2}' | awk '{print $1}'" %(vcpe)
118 cmd_lan = "sudo docker exec %s ip addr show eth1 |grep inet |head -1 | tr -s ' ' | awk '{print $2}' | awk '{print $1}'" %(vcpe)
119 st, output = vsg.run_cmd(cmd_wan, timeout = 30)
120 ip_wan = '0.0.0.0/24'
121 ip_lan = '0.0.0.0/24'
122 if st and output:
123 if cls.ip_addr_pattern.match(output):
124 ip_wan = output
125
126 st, output = vsg.run_cmd(cmd_lan, timeout = 30)
127 if st and output:
128 if cls.ip_addr_pattern.match(output):
129 ip_lan = output
130
131 cls.vcpe_map[vcpe] = { 'vsg': vsg, 'gw': gw, 'wan': ip_wan, 'lan': ip_lan }
132
133 return True
134
135 @classmethod
136 def restore_vcpe_config(cls, vcpe, gw = True, wan = False, lan = False):
137 if vcpe in cls.vcpe_map:
138 vsg = cls.vcpe_map[vcpe]['vsg']
139 cmds = []
140 if gw is True:
141 #restore default gw
142 gw = cls.vcpe_map[vcpe]['gw']
143 cmds.append('sudo docker exec {} ip link set eth0 up'.format(vcpe))
144 cmds.append('sudo docker exec {} route add default gw {} dev eth0'.format(vcpe, gw))
145 if wan is True:
146 ip_wan = cls.vcpe_map[vcpe]['wan']
147 cmds.append('sudo docker exec {} ip addr set {} dev eth0'.format(vcpe, ip_wan))
148 if lan is True:
149 ip_lan = cls.vcpe_map[vcpe]['lan']
150 cmds.append('sudo docker exec {} ip addr set {} dev eth1'.format(vcpe, ip_lan))
151 ret_status = True
152 for cmd in cmds:
153 st, _ = vsg.run_cmd(cmd, timeout = 30)
154 if st == False:
155 ret_status = False
156 return ret_status
157 return False
158
159 @classmethod
160 def get_vcpe_gw(cls, vcpe):
161 if vcpe in cls.vcpe_map:
162 return cls.vcpe_map[vcpe]['gw']
163 return None
164
165 @classmethod
166 def get_vcpe_wan(cls, vcpe):
167 if vcpe in cls.vcpe_map:
168 return cls.vcpe_map[vcpe]['wan']
169 return None
170
171 @classmethod
172 def get_vcpe_lan(cls, vcpe):
173 if vcpe in cls.vcpe_map:
174 return cls.vcpe_map[vcpe]['lan']
175 return None
176
177 @classmethod
178 def vcpe_wan_up(cls, vcpe):
179 return cls.restore_vcpe_config(vcpe)
180
181 @classmethod
182 def vcpe_lan_up(cls, vcpe, vsg = None):
183 if vsg is None:
184 vsg = cls.get_vcpe_vsg(vcpe)
185 if vsg is None:
186 return False
187 cmd = 'sudo docker exec {} ip link set eth1 up'.format(vcpe)
188 st, _ = vsg.run_cmd(cmd, timeout = 30)
189 return st
190
191 #we cannot access compute node if the vcpe port gets dhcp as default would be through fabric
192 @classmethod
193 def vcpe_port_down(cls, vcpe, port, vsg = None):
194 if vsg is None:
195 vsg = cls.get_vcpe_vsg(vcpe)
196 if vsg is None:
197 return False
198 if not cls.save_vcpe_config(vsg, vcpe):
199 return False
200 cmd = 'sudo docker exec {} ip link set {} down'.format(vcpe, port)
201 st, _ = vsg.run_cmd(cmd, timeout = 30)
202 if st is False:
203 cls.restore_vcpe_config(vcpe)
204 return False
205 return st
206
207 @classmethod
208 def vcpe_wan_down(cls, vcpe, vsg = None):
209 return cls.vcpe_port_down(vcpe, 'eth0', vsg = vsg)
210
211 @classmethod
212 def vcpe_lan_down(cls, vcpe, vsg = None):
213 return cls.vcpe_port_down(vcpe, 'eth1', vsg = vsg)
214
215 @classmethod
216 def save_interface_config(cls, intf):
217 if intf not in cls.interface_map:
218 ip = get_ip(intf)
219 if ip is None:
220 ip = '0.0.0.0'
221 default_gw, default_gw_device = get_default_gw()
222 if default_gw_device != intf:
223 default_gw = '0.0.0.0'
224 cls.interface_map[intf] = { 'ip' : ip, 'gw': default_gw }
225 #bounce the interface to remove default gw
226 cmds = ['ifconfig {} 0 down'.format(intf),
227 'ifconfig {} 0 up'.format(intf)
228 ]
229 for cmd in cmds:
230 os.system(cmd)
231
232 #open up access to compute node
233 @classmethod
234 def restore_interface_config(cls, intf, vcpe = None):
235 if intf in cls.interface_map:
236 ip = cls.interface_map[intf]['ip']
237 gw = cls.interface_map[intf]['gw']
238 del cls.interface_map[intf]
239 cmds = []
240 if vcpe is not None:
241 shutil.copy('/etc/resolv.conf.orig', '/etc/resolv.conf')
242 #bounce the vcpes to clear default gw
243 cmds.append('ifconfig {} 0 down'.format(vcpe))
244 cmds.append('ifconfig {} 0 up'.format(vcpe))
245 cmds.append('ifconfig {} {} up'.format(intf, ip))
246 if gw and gw != '0.0.0.0':
247 cmds.append('route add default gw {} dev {}'.format(gw, intf))
248 for cmd in cmds:
249 os.system(cmd)
250
251 @classmethod
252 def vcpe_get_dhcp(cls, vcpe, mgmt = 'eth0'):
253 '''Get DHCP from vcpe dhcp interface.'''
254 '''We have to also save the management interface config for restoration'''
255 cls.save_interface_config(mgmt)
256 getstatusoutput('pkill -9 dhclient')
257 st, output = getstatusoutput('dhclient -q {}'.format(vcpe))
258 getstatusoutput('pkill -9 dhclient')
259 vcpe_ip = get_ip(vcpe)
260 if vcpe_ip is None:
261 cls.restore_interface_config(mgmt)
262 return None
263 if output:
264 #workaround for docker container apparmor that prevents moving dhclient resolv.conf
265 start = output.find('/etc/resolv.conf')
266 if start >= 0:
267 end = output.find("'", start)
268 dns_file = output[start:end]
269 if os.access(dns_file, os.F_OK):
270 shutil.copy(dns_file, '/etc/resolv.conf')
271
272 default_gw, default_gw_device = get_default_gw()
273 if default_gw and default_gw_device == vcpe:
274 return vcpe_ip
275 cls.restore_interface_config(mgmt, vcpe = vcpe)
276 return None
277
278class VSGWrapper(object):
279
280 def __init__(self, vsg):
281 self.vsg = vsg
282 self.name = self.vsg.name
283 self.compute_node = self.get_compute_node()
284 self.ip = self.get_ip()
285
286 def get_compute_node(self):
287 return self.vsg._info['OS-EXT-SRV-ATTR:hypervisor_hostname']
288
289 def get_ip(self):
290 if 'management' in self.vsg.networks:
291 ips = self.vsg.networks['management']
292 if len(ips) > 0:
293 return ips[0]
294 return None
295
296 def run_cmd_compute(self, cmd, timeout = 5):
297 ssh_agent = SSHTestAgent(self.compute_node)
298 st, output = ssh_agent.run_cmd(cmd, timeout = timeout)
299 if st == True and output:
300 output = output.strip()
301 else:
302 output = None
303
304 return st, output
305
306 def run_cmd(self, cmd, timeout = 5, mgmt = 'eth0'):
307 last_gw = VSGAccess.open_mgmt(mgmt)
308 ssh_agent = SSHTestAgent(self.compute_node)
309 ssh_cmd = 'ssh {} {}'.format(self.ip, cmd)
310 st, output = ssh_agent.run_cmd(ssh_cmd, timeout = timeout)
311 if st == True and output:
312 output = output.strip()
313 else:
314 output = None
315 VSGAccess.close_mgmt(last_gw, mgmt)
316 return st, output
317
318 def get_health(self):
319 if self.ip is None:
320 return False
321 cmd = 'ping -c 1 {}'.format(self.ip)
322 st, _ = self.run_cmd_compute(cmd)
323 return st
324
325 def check_access(self):
326 if self.ip is None:
327 return False
328 ssh_agent = SSHTestAgent(self.compute_node)
329 st, _ = ssh_agent.run_cmd('ls', timeout=10)
330 if st == False:
331 return st
332 st, _ = ssh_agent.run_cmd('ssh {} ls'.format(self.ip), timeout=30)
333 return st