blob: 644d4879bb83c5d4170a79ce28864e73d2f85209 [file] [log] [blame]
Matteo Scandolo48d3d2d2017-08-08 13:05:27 -07001
2# Copyright 2017-present Open Networking Foundation
3#
4# Licensed under the Apache License, Version 2.0 (the "License");
5# you may not use this file except in compliance with the License.
6# You may obtain a copy of the License at
7#
8# http://www.apache.org/licenses/LICENSE-2.0
9#
10# Unless required by applicable law or agreed to in writing, software
11# distributed under the License is distributed on an "AS IS" BASIS,
12# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13# See the License for the specific language governing permissions and
14# limitations under the License.
15
16
A R Karthick35495c32017-05-11 14:58:32 -070017import requests
18import json
19import time
A.R Karthick57fa9372017-05-24 12:47:03 -070020import os
21import signal
A.R Karthick4c4d0492017-05-26 19:23:05 -070022from CordTestUtils import log_test as log, getstatusoutput, get_controller
A R Karthickbf1e4b02017-07-11 20:17:14 -070023from CordContainer import Container, Onos
A.R Karthick4c4d0492017-05-26 19:23:05 -070024from OnosCtrl import OnosCtrl
A.R Karthick4f583842017-06-09 17:15:47 -070025from OltConfig import OltConfig
A.R Karthick57fa9372017-05-24 12:47:03 -070026
27class VolthaService(object):
28 services = ('consul', 'kafka', 'zookeeper', 'registrator', 'fluentd')
29 compose_file = 'docker-compose-system-test.yml'
30 service_map = {}
31
A.R Karthick4f583842017-06-09 17:15:47 -070032 def __init__(self, voltha_loc, controller, interface = 'eth0', olt_config = None):
A.R Karthick57fa9372017-05-24 12:47:03 -070033 if not os.access(voltha_loc, os.F_OK):
34 raise Exception('Voltha location %s not found' %voltha_loc)
35 compose_file_loc = os.path.join(voltha_loc, 'compose', self.compose_file)
36 if not os.access(compose_file_loc, os.F_OK):
37 raise Exception('Voltha compose file %s not found' %compose_file_loc)
38 self.voltha_loc = voltha_loc
39 self.controller = controller
40 self.interface = interface
41 self.compose_file_loc = compose_file_loc
A.R Karthick4f583842017-06-09 17:15:47 -070042 num_onus = 1
43 if olt_config is not None:
44 port_map, _ = OltConfig(olt_config).olt_port_map()
45 if port_map['ponsim'] is True:
46 num_onus = max(1, len(port_map['ports']))
47 self.num_onus = num_onus
A.R Karthick57fa9372017-05-24 12:47:03 -070048
49 def start(self):
50 start_cmd = 'docker-compose -f {} up -d {} {} {} {} {}'.format(self.compose_file_loc,
51 *self.services)
52 ret = os.system(start_cmd)
53 if ret != 0:
54 raise Exception('Failed to start voltha services. Failed with code %d' %ret)
55
56 for service in self.services:
57 name = 'compose_{}_1'.format(service)
58 network = 'compose_default'
59 cnt = Container(name, name)
60 ip = cnt.ip(network = network)
61 if not ip:
62 raise Exception('IP not found for container %s' %name)
63 print('IP %s for service %s' %(ip, service))
64 self.service_map[service] = dict(name = name, network = network, ip = ip)
65
66 #first start chameleon
67 chameleon_start_cmd = "cd {} && sh -c '. ./env.sh && \
68 nohup python chameleon/main.py -v --consul=localhost:8500 \
69 --fluentd={}:24224 --grpc-endpoint=localhost:50555 \
70 >/tmp/chameleon.log 2>&1 &'".format(self.voltha_loc,
71 self.service_map['fluentd']['ip'])
A.R Karthickc2697a12017-05-24 14:01:15 -070072 if not self.service_running('python chameleon/main.py'):
73 ret = os.system(chameleon_start_cmd)
74 if ret != 0:
75 raise Exception('VOLTHA chameleon service not started. Failed with return code %d' %ret)
A R Karthickd69d5702017-07-25 11:31:47 -070076 time.sleep(10)
A.R Karthickc2697a12017-05-24 14:01:15 -070077 else:
78 print('Chameleon voltha sevice is already running. Skipped start')
A.R Karthick57fa9372017-05-24 12:47:03 -070079
80 #now start voltha and ofagent
A R Karthickd52ca8a2017-07-24 17:38:55 -070081 voltha_setup_cmd = "cd {} && sh -c '. ./env.sh && make rebuild-venv && make protos'".format(self.voltha_loc)
A.R Karthick57fa9372017-05-24 12:47:03 -070082 voltha_start_cmd = "cd {} && sh -c '. ./env.sh && \
83 nohup python voltha/main.py -v --consul=localhost:8500 --kafka={}:9092 -I {} \
84 --fluentd={}:24224 --rest-port=8880 --grpc-port=50555 \
85 >/tmp/voltha.log 2>&1 &'".format(self.voltha_loc,
86 self.service_map['kafka']['ip'],
87 self.interface,
88 self.service_map['fluentd']['ip'])
A R Karthickd52ca8a2017-07-24 17:38:55 -070089 pki_dir = '{}/pki'.format(self.voltha_loc)
A.R Karthickc2697a12017-05-24 14:01:15 -070090 if not self.service_running('python voltha/main.py'):
A R Karthickd52ca8a2017-07-24 17:38:55 -070091 voltha_pki_dir = '/voltha'
92 if os.access(pki_dir, os.F_OK):
93 pki_xfer_cmd = 'mkdir -p {} && cp -rv {}/pki {}'.format(voltha_pki_dir,
94 self.voltha_loc,
95 voltha_pki_dir)
96 os.system(pki_xfer_cmd)
97 #os.system(voltha_setup_cmd)
A.R Karthickc2697a12017-05-24 14:01:15 -070098 ret = os.system(voltha_start_cmd)
99 if ret != 0:
100 raise Exception('Failed to start VOLTHA. Return code %d' %ret)
A R Karthickd69d5702017-07-25 11:31:47 -0700101 time.sleep(10)
A.R Karthickc2697a12017-05-24 14:01:15 -0700102 else:
103 print('VOLTHA core is already running. Skipped start')
A.R Karthick57fa9372017-05-24 12:47:03 -0700104
105 ofagent_start_cmd = "cd {} && sh -c '. ./env.sh && \
106 nohup python ofagent/main.py -v --consul=localhost:8500 \
107 --fluentd={}:24224 --controller={}:6653 --grpc-endpoint=localhost:50555 \
108 >/tmp/ofagent.log 2>&1 &'".format(self.voltha_loc,
109 self.service_map['fluentd']['ip'],
110 self.controller)
A.R Karthickc2697a12017-05-24 14:01:15 -0700111 if not self.service_running('python ofagent/main.py'):
A R Karthickd52ca8a2017-07-24 17:38:55 -0700112 ofagent_pki_dir = '/ofagent'
113 if os.access(pki_dir, os.F_OK):
114 pki_xfer_cmd = 'mkdir -p {} && cp -rv {}/pki {}'.format(ofagent_pki_dir,
115 self.voltha_loc,
116 ofagent_pki_dir)
117 os.system(pki_xfer_cmd)
A.R Karthickc2697a12017-05-24 14:01:15 -0700118 ret = os.system(ofagent_start_cmd)
119 if ret != 0:
120 raise Exception('VOLTHA ofagent not started. Failed with return code %d' %ret)
A R Karthickd69d5702017-07-25 11:31:47 -0700121 time.sleep(10)
A.R Karthickc2697a12017-05-24 14:01:15 -0700122 else:
123 print('VOLTHA ofagent is already running. Skipped start')
124
A.R Karthick12e08c32017-05-30 17:09:26 -0700125 ponsim_start_cmd = "cd {} && sh -c '. ./env.sh && \
A.R Karthick4f583842017-06-09 17:15:47 -0700126 nohup python ponsim/main.py -o {} -v >/tmp/ponsim.log 2>&1 &'".format(self.voltha_loc, self.num_onus)
A.R Karthick12e08c32017-05-30 17:09:26 -0700127 if not self.service_running('python ponsim/main.py'):
128 ret = os.system(ponsim_start_cmd)
129 if ret != 0:
130 raise Exception('PONSIM not started. Failed with return code %d' %ret)
131 time.sleep(3)
132 else:
133 print('PONSIM already running. Skipped start')
134
A.R Karthickc2697a12017-05-24 14:01:15 -0700135 def service_running(self, pattern):
136 st, _ = getstatusoutput('pgrep -f "{}"'.format(pattern))
137 return True if st == 0 else False
A.R Karthick57fa9372017-05-24 12:47:03 -0700138
139 def kill_service(self, pattern):
140 st, output = getstatusoutput('pgrep -f "{}"'.format(pattern))
141 if st == 0 and output:
142 pids = output.strip().splitlines()
143 for pid in pids:
144 try:
145 os.kill(int(pid), signal.SIGKILL)
146 except:
147 pass
148
149 def stop(self):
150 self.kill_service('python voltha/main.py')
151 self.kill_service('python ofagent/main.py')
152 self.kill_service('python chameleon/main.py')
A.R Karthick12e08c32017-05-30 17:09:26 -0700153 self.kill_service('python ponsim/main.py')
A.R Karthick57fa9372017-05-24 12:47:03 -0700154 service_stop_cmd = 'docker-compose -f {} down'.format(self.compose_file_loc)
155 os.system(service_stop_cmd)
A R Karthick35495c32017-05-11 14:58:32 -0700156
157class VolthaCtrl(object):
A R Karthick53442712017-07-27 12:23:30 -0700158 UPLINK_VLAN_START = 333
A.R Karthick4c4d0492017-05-26 19:23:05 -0700159 UPLINK_VLAN_MAP = { 'of:0000000000000001' : '222' }
A R Karthickbf1e4b02017-07-11 20:17:14 -0700160 REST_PORT = 8881
A R Karthick53442712017-07-27 12:23:30 -0700161 HOST = '172.17.0.1'
A R Karthick6e70e142017-07-28 15:25:38 -0700162 ONOS_APPS = ('org.onosproject.dhcp', 'org.onosproject.dhcp-relay', 'org.ciena.cordigmp')
A.R Karthick4c4d0492017-05-26 19:23:05 -0700163
A R Karthick53442712017-07-27 12:23:30 -0700164 def __init__(self, host = HOST, rest_port = REST_PORT, uplink_vlan_map = UPLINK_VLAN_MAP, uplink_vlan_start = UPLINK_VLAN_START):
A R Karthick35495c32017-05-11 14:58:32 -0700165 self.host = host
166 self.rest_port = rest_port
167 self.rest_url = 'http://{}:{}/api/v1'.format(host, rest_port)
A.R Karthick4c4d0492017-05-26 19:23:05 -0700168 self.uplink_vlan_map = uplink_vlan_map
A R Karthick53442712017-07-27 12:23:30 -0700169 VolthaCtrl.UPLINK_VLAN_START = uplink_vlan_start
A.R Karthick4c4d0492017-05-26 19:23:05 -0700170 self.switches = []
171 self.switch_map = {}
172
173 def config(self, fake = False):
174 devices = OnosCtrl.get_devices()
175 if not devices:
176 return self.switch_map
177 voltha_devices = filter(lambda d: not d['mfr'].startswith('Nicira'), devices)
178 self.switches = voltha_devices
179 device_config = { 'devices' : { } }
180 device_id = None
181 for device in voltha_devices:
182 device_id = device['id']
183 ports = OnosCtrl.get_ports_device(device_id)
184 nni_ports = filter(lambda p: p['isEnabled'] and 'annotations' in p and p['annotations']['portName'].startswith('nni'), ports)
185 uni_ports = filter(lambda p: p['isEnabled'] and 'annotations' in p and p['annotations']['portName'].startswith('uni'), ports)
186 if device_id not in self.uplink_vlan_map:
A R Karthick53442712017-07-27 12:23:30 -0700187 uplink_vlan = VolthaCtrl.UPLINK_VLAN_START
188 VolthaCtrl.UPLINK_VLAN_START += 1
189 log.info('Voltha device %s not in map. Using uplink vlan %d' %(device_id, uplink_vlan))
190 else:
191 uplink_vlan = self.uplink_vlan_map[device_id]
A.R Karthick4c4d0492017-05-26 19:23:05 -0700192 if not nni_ports:
193 log.info('Voltha device %s has no NNI ports' %device_id)
194 if fake is True:
195 log.info('Faking NNI port 0')
196 nni_ports = [ {'port': '0'} ]
197 else:
198 log.info('Skip configuring device %s' %device_id)
199 continue
200 if not uni_ports:
201 log.info('Voltha device %s has no UNI ports' %device_id)
202 if fake is True:
203 log.info('Faking UNI port 252')
204 uni_ports = [ {'port': '252'} ]
205 else:
206 log.info('Skip configuring device %s' %device_id)
207 continue
A.R Karthick4c4d0492017-05-26 19:23:05 -0700208 onu_ports = map(lambda uni: uni['port'], uni_ports)
209 self.switch_map[device_id] = dict(uplink_vlan = uplink_vlan, ports = onu_ports)
210 device_config['devices'][device_id] = {}
211 device_config['devices'][device_id]['basic'] = dict(driver='pmc-olt')
212 device_config['devices'][device_id]['accessDevice'] = dict(uplink=nni_ports[0]['port'],
213 vlan = uplink_vlan,
214 defaultVlan='0'
215 )
216 if device_id:
217 #toggle drivers/openflow base before reconfiguring the driver and olt config data
218 OnosCtrl('org.onosproject.drivers').deactivate()
219 OnosCtrl('org.onosproject.openflow-base').deactivate()
220 OnosCtrl.config(device_config)
A R Karthick6e70e142017-07-28 15:25:38 -0700221 time.sleep(10)
A.R Karthick4c4d0492017-05-26 19:23:05 -0700222 OnosCtrl('org.onosproject.drivers').activate()
223 OnosCtrl('org.onosproject.openflow-base').activate()
224 time.sleep(5)
A R Karthick6e70e142017-07-28 15:25:38 -0700225 log.info('Reactivating CORD and ONOS apps')
226 Onos.activate_cord_apps(deactivate = True)
227 Onos.activate_apps(self.ONOS_APPS, deactivate = True)
A.R Karthick4c4d0492017-05-26 19:23:05 -0700228
229 return self.switch_map
A R Karthick35495c32017-05-11 14:58:32 -0700230
231 def get_devices(self):
232 url = '{}/local/devices'.format(self.rest_url)
233 resp = requests.get(url)
234 if resp.ok is not True or resp.status_code != 200:
235 return None
236 return resp.json()
237
A.R Karthick8b9c5f12017-05-30 17:47:08 -0700238 def enable_device(self, olt_type, olt_mac = None, address = None):
A R Karthick35495c32017-05-11 14:58:32 -0700239 url = '{}/local/devices'.format(self.rest_url)
A.R Karthick8b9c5f12017-05-30 17:47:08 -0700240 if olt_mac is None and address is None:
241 log.error('Either olt mac or address needs to be specified')
A.R Karthick8a507cf2017-06-02 18:44:49 -0700242 return None, False
A.R Karthick8b9c5f12017-05-30 17:47:08 -0700243 if olt_mac is not None:
244 device_config = { 'type' : olt_type, 'mac_address' : olt_mac }
245 else:
A R Karthick31a40172017-08-14 12:06:09 -0700246 if len(address.split(':')) > 1:
247 device_config = { 'type' : olt_type, 'host_and_port' : address }
248 else:
249 device_config = { 'type' : olt_type, 'ipv4_address' : address }
A R Karthick35495c32017-05-11 14:58:32 -0700250 #pre-provision
A.R Karthick8b9c5f12017-05-30 17:47:08 -0700251 if olt_mac is not None:
252 log.info('Pre-provisioning %s with mac %s' %(olt_type, olt_mac))
253 else:
254 log.info('Pre-provisioning %s with address %s' %(olt_type, address))
A R Karthick35495c32017-05-11 14:58:32 -0700255 resp = requests.post(url, data = json.dumps(device_config))
256 if resp.ok is not True or resp.status_code != 200:
A.R Karthick8a507cf2017-06-02 18:44:49 -0700257 return None, False
A R Karthick35495c32017-05-11 14:58:32 -0700258 device_id = resp.json()['id']
259 log.info('Enabling device %s' %(device_id))
260 enable_url = '{}/{}/enable'.format(url, device_id)
261 resp = requests.post(enable_url)
262 if resp.ok is not True or resp.status_code != 200:
A.R Karthick8a507cf2017-06-02 18:44:49 -0700263 return None, False
A R Karthick35495c32017-05-11 14:58:32 -0700264 #get operational status
A R Karthick090631b2017-07-25 16:05:05 -0700265 time.sleep(10)
A R Karthick35495c32017-05-11 14:58:32 -0700266 log.info('Checking operational status for device %s' %(device_id))
267 resp = requests.get('{}/{}'.format(url, device_id))
268 if resp.ok is not True or resp.status_code != 200:
A.R Karthick8a507cf2017-06-02 18:44:49 -0700269 return device_id, False
A R Karthick35495c32017-05-11 14:58:32 -0700270 device_info = resp.json()
271 if device_info['oper_status'] != 'ACTIVE' or \
272 device_info['admin_state'] != 'ENABLED' or \
273 device_info['connect_status'] != 'REACHABLE':
A.R Karthick8a507cf2017-06-02 18:44:49 -0700274 return device_id, False
A R Karthick35495c32017-05-11 14:58:32 -0700275
A.R Karthick8a507cf2017-06-02 18:44:49 -0700276 return device_id, True
277
278 def disable_device(self, device_id, delete = True):
279 log.info('Disabling device %s' %(device_id))
280 disable_url = '{}/local/devices/{}/disable'.format(self.rest_url, device_id)
281 resp = requests.post(disable_url)
282 if resp.ok is not True or resp.status_code != 200:
283 return False
284 if delete is True:
A R Karthickd69d5702017-07-25 11:31:47 -0700285 #rest for disable completion
286 time.sleep(10)
A.R Karthick8a507cf2017-06-02 18:44:49 -0700287 log.info('Deleting device %s' %(device_id))
288 delete_url = '{}/local/devices/{}/delete'.format(self.rest_url, device_id)
289 resp = requests.delete(delete_url)
290 if resp.status_code not in [204, 202, 200]:
291 return False
A R Karthick35495c32017-05-11 14:58:32 -0700292 return True
Chetan Gaonker3620a112017-05-23 06:10:15 +0000293
Thangavelu K Se6c77c72017-06-23 21:28:48 +0000294 def restart_device(self, device_id):
295 log.info('Restarting olt or onu device %s' %(device_id))
296 disable_url = '{}/local/devices/{}/restart'.format(self.rest_url, device_id)
297 resp = requests.post(disable_url)
298 if resp.ok is not True or resp.status_code != 200:
299 return False
300 return True
301
302 def pause_device(self, device_id):
303 log.info('Restarting olt or onu device %s' %(device_id))
304 disable_url = '{}/local/devices/{}/pause'.format(self.rest_url, device_id)
305 resp = requests.post(disable_url)
306 if resp.ok is not True or resp.status_code != 200:
307 return False
308 return True
309
Chetan Gaonker3620a112017-05-23 06:10:15 +0000310 def get_operational_status(self, device_id):
311 url = '{}/local/devices'.format(self.rest_url)
312 log.info('Checking operational status for device %s' %(device_id))
313 resp = requests.get('{}/{}'.format(url, device_id))
314 if resp.ok is not True or resp.status_code != 200:
315 return False
316 device_info = resp.json()
317 if device_info['oper_status'] != 'ACTIVE' or \
318 device_info['admin_state'] != 'ENABLED' or \
319 device_info['connect_status'] != 'REACHABLE':
320 return False
321 return True
322
323 def check_preprovision_status(self, device_id):
324 url = '{}/local/devices'.format(self.rest_url)
325 log.info('Check if device %s is in Preprovisioning state'%(device_id))
326 resp = requests.get('{}/{}'.format(url, device_id))
327 if resp.ok is not True or resp.status_code != 200:
328 return False
329 device_info = resp.json()
330 if device_info['admin_status'] == 'PREPROVISIONED':
331 return True
332 return False
A R Karthickbf1e4b02017-07-11 20:17:14 -0700333
334def get_olt_app():
335 our_path = os.path.dirname(os.path.realpath(__file__))
336 version = Onos.getVersion()
337 major = int(version.split('.')[0])
338 minor = int(version.split('.')[1])
339 olt_app_version = '1.2-SNAPSHOT'
340 if major > 1:
341 olt_app_version = '2.0-SNAPSHOT'
342 elif major == 1:
343 if minor > 10:
344 olt_app_version = '2.0-SNAPSHOT'
345 elif minor <= 8:
346 olt_app_version = '1.1-SNAPSHOT'
347 olt_app_file = os.path.join(our_path, '..', 'apps/olt-app-{}.oar'.format(olt_app_version))
348 return olt_app_file
349
A R Karthick31a40172017-08-14 12:06:09 -0700350def voltha_setup(host = '172.17.0.1', olt_ip = None, rest_port = VolthaCtrl.REST_PORT,
A R Karthick9dc6e922017-07-12 14:40:16 -0700351 olt_type = 'ponsim_olt', olt_mac = '00:0c:e2:31:12:00',
A R Karthickbf1e4b02017-07-11 20:17:14 -0700352 uplink_vlan_map = VolthaCtrl.UPLINK_VLAN_MAP,
A R Karthick53442712017-07-27 12:23:30 -0700353 uplink_vlan_start = VolthaCtrl.UPLINK_VLAN_START,
A R Karthickbf1e4b02017-07-11 20:17:14 -0700354 config_fake = False, olt_app = None):
355
A R Karthick53442712017-07-27 12:23:30 -0700356 voltha = VolthaCtrl(host, rest_port = rest_port,
357 uplink_vlan_map = uplink_vlan_map,
358 uplink_vlan_start = uplink_vlan_start)
A R Karthickbf1e4b02017-07-11 20:17:14 -0700359 if olt_type.startswith('ponsim'):
360 ponsim_address = '{}:50060'.format(host)
361 log.info('Enabling ponsim olt')
362 device_id, status = voltha.enable_device(olt_type, address = ponsim_address)
363 else:
A R Karthick31a40172017-08-14 12:06:09 -0700364 if olt_type.startswith('maple'):
365 if olt_ip:
366 log.info('Enabling %s' %olt_type)
367 device_id, status = voltha.enable_device(olt_type, address = olt_ip)
368 else:
369 log.info('OLT IP needs to be specified for maple olt')
370 else:
371 log.info('Enabling OLT instance for %s with mac %s' %(olt_type, olt_mac))
372 device_id, status = voltha.enable_device(olt_type, olt_mac)
A R Karthickbf1e4b02017-07-11 20:17:14 -0700373
374 if device_id is None or status is False:
A R Karthick9dc6e922017-07-12 14:40:16 -0700375 if device_id:
376 voltha.disable_device(device_id)
A R Karthickbf1e4b02017-07-11 20:17:14 -0700377 return None
378
379 switch_map = None
380 olt_installed = False
381 if olt_app is None:
382 olt_app = get_olt_app()
383 try:
A R Karthick090631b2017-07-25 16:05:05 -0700384 time.sleep(5)
A R Karthickbf1e4b02017-07-11 20:17:14 -0700385 switch_map = voltha.config(fake = config_fake)
386 if switch_map is None:
387 voltha.disable_device(device_id)
388 return None
389 log.info('Installing OLT app %s' %olt_app)
390 OnosCtrl.install_app(olt_app)
391 olt_installed = True
392 time.sleep(5)
393 return voltha, device_id, switch_map
394 except:
395 voltha.disable_device(device_id)
396 time.sleep(10)
397 if olt_installed is True:
398 log.info('Uninstalling OLT app %s' %olt_app)
399 OnosCtrl.uninstall_app(olt_app)
400
401 return None
402
403def voltha_teardown(voltha_ctrl, device_id, switch_map, olt_app = None):
404 voltha_ctrl.disable_device(device_id)
405 time.sleep(10)
406 if olt_app is None:
407 olt_app = get_olt_app()
A R Karthickdd064632017-07-12 13:02:17 -0700408 log.info('Uninstalling OLT app %s' %olt_app)
A R Karthickbf1e4b02017-07-11 20:17:14 -0700409 OnosCtrl.uninstall_app(olt_app)