blob: e9e881a95f7c68186b01aa97d2dbc5d35cfdbd83 [file] [log] [blame]
Zsolt Harasztia133a452016-12-22 01:26:57 -08001#!/usr/bin/env python
2#
3# Copyright 2016 the original author or authors.
4#
5# Licensed under the Apache License, Version 2.0 (the "License");
6# you may not use this file except in compliance with the License.
7# You may obtain a copy of the License at
8#
9# http://www.apache.org/licenses/LICENSE-2.0
10#
11# Unless required by applicable law or agreed to in writing, software
12# distributed under the License is distributed on an "AS IS" BASIS,
13# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14# See the License for the specific language governing permissions and
15# limitations under the License.
16#
Zsolt Harasztia133a452016-12-22 01:26:57 -080017import readline
Zsolt Harasztid036b7e2016-12-23 15:36:01 -080018import sys
19from optparse import make_option
20from time import sleep
21
Zsolt Harasztia133a452016-12-22 01:26:57 -080022import grpc
Zsolt Harasztid036b7e2016-12-23 15:36:01 -080023import requests
24from cmd2 import Cmd, options
25from google.protobuf.empty_pb2 import Empty
Zsolt Harasztia133a452016-12-22 01:26:57 -080026from simplejson import dumps
27
Zsolt Harasztid036b7e2016-12-23 15:36:01 -080028from cli.device import DeviceCli
29from cli.logical_device import LogicalDeviceCli
30from voltha.core.flow_decomposer import *
Zsolt Harasztia133a452016-12-22 01:26:57 -080031from voltha.protos import third_party
32from voltha.protos import voltha_pb2
Zsolt Harasztid036b7e2016-12-23 15:36:01 -080033from voltha.protos.openflow_13_pb2 import FlowTableUpdate
34
Zsolt Harasztia133a452016-12-22 01:26:57 -080035_ = third_party
Zsolt Harasztid036b7e2016-12-23 15:36:01 -080036from cli.utils import pb2dict
Zsolt Harasztia133a452016-12-22 01:26:57 -080037
38
Zsolt Harasztid036b7e2016-12-23 15:36:01 -080039banner = """\
40 _ _ _ _ _
41 __ _____| | |_| |_ __ _ __| (_)
42 \ V / _ \ | _| ' \/ _` | / _| | |
43 \_/\___/_|\__|_||_\__,_| \__|_|_|
44(to exit type q, exit or quit or hit Ctrl-D)
45"""
Zsolt Harasztia133a452016-12-22 01:26:57 -080046
47class VolthaCli(Cmd):
48
49 prompt = 'voltha'
50 history_file_name = '.voltha_cli_history'
Zsolt Harasztid036b7e2016-12-23 15:36:01 -080051
52 # Settable CLI parameters
53 voltha_grpc = 'localhost:50055'
54 voltha_sim_rest = 'localhost:18880'
Zsolt Harasztia133a452016-12-22 01:26:57 -080055 max_history_lines = 500
Zsolt Harasztid036b7e2016-12-23 15:36:01 -080056 default_device_id = None
57 default_logical_device_id = None
Zsolt Harasztia133a452016-12-22 01:26:57 -080058
59 Cmd.settable.update(dict(
Zsolt Harasztid036b7e2016-12-23 15:36:01 -080060 voltha_grpc='Voltha GRPC endpoint in form of <host>:<port>',
61 voltha_sim_rest='Voltha simulation back door for testing in form '
62 'of <host>:<port>',
63 max_history_lines='Maximum number of history lines stored across '
64 'sessions',
65 default_device_id='Device id used when no device id is specified',
66 default_logical_device_id='Logical device id used when no device id '
67 'is specified',
Zsolt Harasztia133a452016-12-22 01:26:57 -080068 ))
69
Zsolt Harasztia133a452016-12-22 01:26:57 -080070 def __init__(self, *args, **kw):
71 Cmd.__init__(self, *args, **kw)
72 self.prompt = '(' + self.colorize(
73 self.colorize(self.prompt, 'red'), 'bold') + ') '
74 self.channel = None
75
76 def load_history(self):
77 """Load saved command history from local history file"""
78 try:
79 with file(self.history_file_name, 'r') as f:
80 for line in f.readlines():
81 stripped_line = line.strip()
82 self.history.append(stripped_line)
83 readline.add_history(stripped_line)
84 except IOError:
85 pass # ignore if file cannot be read
86
87 def save_history(self):
88 try:
89 with file(self.history_file_name, 'w') as f:
90 f.write('\n'.join(self.history[-self.max_history_lines:]))
91 except IOError, e:
92 print >> sys.stderr, 'Could not save history in {}: {}'.format(
93 self.history_file_name, e.msg)
94 else:
95 print >> sys.stderr, 'History saved as {}'.format(
96 self.history_file_name)
97
98 def get_channel(self):
99 if self.channel is None:
100 self.channel = grpc.insecure_channel(self.voltha_grpc)
101 return self.channel
102
Zsolt Harasztid036b7e2016-12-23 15:36:01 -0800103 def preloop(self):
104 self.poutput(banner)
105
Zsolt Harasztia133a452016-12-22 01:26:57 -0800106 def do_reset_history(self, arg):
107 """Reset CLI history"""
108 while self.history:
109 self.history.pop()
110
111 def do_launch(self, arg):
112 """If Voltha is not running yet, launch it"""
113 pass
114
115 def do_restart(self, arg):
116 """Launch Voltha, but if it is already running, terminate it first"""
117 pass
118
119 def do_devices(self, arg):
120 """List devices registered in Voltha"""
121 stub = voltha_pb2.VolthaLocalServiceStub(self.get_channel())
122 res = stub.ListDevices(Empty())
123 for device in res.items:
124 print self.colorize('# ====== device {}'.format(device.id), 'blue')
125 print dumps(pb2dict(device), indent=4, sort_keys=True)
126
127 def do_logical_devices(self, arg):
128 """List logical devices in Voltha"""
129 stub = voltha_pb2.VolthaLocalServiceStub(self.get_channel())
130 res = stub.ListLogicalDevices(Empty())
131 for logical_device in res.items:
132 print self.colorize('# ====== logical device {}'.format(
133 logical_device.id), 'blue')
134 print dumps(pb2dict(logical_device), indent=4, sort_keys=True)
135
136 def do_device(self, arg):
137 """Enter device level command mode"""
Zsolt Harasztid036b7e2016-12-23 15:36:01 -0800138 device_id = arg or self.default_device_id
139 if not device_id:
140 raise Exception('<device-id> parameter needed')
141 sub = DeviceCli(self.get_channel, device_id)
Zsolt Harasztia133a452016-12-22 01:26:57 -0800142 sub.cmdloop()
143
144 def do_logical_device(self, arg):
145 """Enter logical device level command mode"""
Zsolt Harasztid036b7e2016-12-23 15:36:01 -0800146 logical_device_id = arg or self.default_logical_device_id
147 if not logical_device_id:
148 raise Exception('<logical-device-id> parameter needed')
149 sub = LogicalDeviceCli(self.get_channel, logical_device_id)
Zsolt Harasztia133a452016-12-22 01:26:57 -0800150 sub.cmdloop()
151
152 def do_debug(self, arg):
153 """Launch PDB debug prompt in CLI (for CLI development)"""
154 from pdb import set_trace
155 set_trace()
156
157 def do_health(self, arg):
158 """Show connectivity status to Voltha status"""
159 stub = voltha_pb2.HealthServiceStub(self.get_channel())
160 res = stub.GetHealthStatus(Empty())
161 print dumps(pb2dict(res), indent=4)
162
Zsolt Harasztid036b7e2016-12-23 15:36:01 -0800163 def do_test(self, arg):
164 """Enter test mode, which makes a bunch on new commands available"""
165 sub = TestCli(self.history, self.get_channel)
166 sub.cmdloop()
Zsolt Harasztia133a452016-12-22 01:26:57 -0800167
Zsolt Harasztia133a452016-12-22 01:26:57 -0800168
Zsolt Harasztid036b7e2016-12-23 15:36:01 -0800169class TestCli(VolthaCli):
170
171 def __init__(self, history, get_channel):
172 VolthaCli.__init__(self)
173 self.history = history
Zsolt Harasztia133a452016-12-22 01:26:57 -0800174 self.get_channel = get_channel
Zsolt Harasztid036b7e2016-12-23 15:36:01 -0800175 self.prompt = '(' + self.colorize(self.colorize('test', 'cyan'),
Zsolt Harasztia133a452016-12-22 01:26:57 -0800176 'bold') + ') '
177
Zsolt Harasztid036b7e2016-12-23 15:36:01 -0800178 @options([
179 make_option('-t', '--device-type', action="store", dest='device_type',
180 help="Device type", default='simulated_olt'),
181 make_option('-m', '--mac-address', action='store', dest='mac_address',
182 default='00:0c:e2:31:40:00'),
183 make_option('-i', '--ip-address', action='store', dest='ip_address'),
184 ])
185 def do_preprovision_olt(self, arg, opts):
186 """Preprovision a new OLT with given device type"""
Zsolt Harasztia133a452016-12-22 01:26:57 -0800187 stub = voltha_pb2.VolthaLocalServiceStub(self.get_channel())
Zsolt Harasztid036b7e2016-12-23 15:36:01 -0800188 kw = dict(type=opts.device_type)
189 if opts.ip_address:
190 kw['ipv4_address'] = opts.ip_address
191 elif opts.mac_address:
192 kw['mac_address'] = opts.mac_address
193 else:
194 raise Exception('Either IP address or Mac Address is needed')
195 device = voltha_pb2.Device(**kw)
196 device = stub.CreateDevice(device)
197 print 'success (device id = {})'.format(device.id)
198 self.default_device_id = device.id
Zsolt Harasztia133a452016-12-22 01:26:57 -0800199
Zsolt Harasztid036b7e2016-12-23 15:36:01 -0800200 def do_activate_olt(self, arg):
201 """
202 Activate an OLT. If the <id> is not provided, it will be on the last
203 pre-provisioned OLT.
204 """
205 device_id = arg or self.default_device_id
206 print 'activating', device_id
207 stub = voltha_pb2.VolthaLocalServiceStub(self.get_channel())
208 stub.ActivateDevice(voltha_pb2.ID(id=device_id))
Zsolt Harasztia133a452016-12-22 01:26:57 -0800209
Zsolt Harasztid036b7e2016-12-23 15:36:01 -0800210 # try to acquire logical device id
211 while True:
212 device = stub.GetDevice(voltha_pb2.ID(id=device_id))
213 if device.oper_status == voltha_pb2.OperStatus.ACTIVE:
214 assert device.parent_id
215 self.default_logical_device_id = device.parent_id
216 break
217 print 'waiting for device to be activated...'
218 sleep(1)
219 print 'success (logical device id = {})'.format(
220 self.default_logical_device_id)
221
222 def do_arrive_onus(self, arg):
223 """
224 Simulate the arrival of ONUs
225 """
226 device_id = arg or self.default_device_id
227 requests.get('http://{}/devices/{}/detect_onus'.format(
228 self.voltha_sim_rest, device_id
229 ))
230
231 def do_install_eapol_flow(self, arg):
232 """
233 Install an EAPOL flow on the given logical device. If device is not
234 given, it will be applied to logical device of the last pre-provisioned
235 OLT device.
236 """
237 logical_device_id = arg or self.default_logical_device_id
238 update = FlowTableUpdate(
239 id=logical_device_id,
240 flow_mod = mk_simple_flow_mod(
241 priority=2000,
242 match_fields=[in_port(101), eth_type(0x888e)],
243 actions=[
244 push_vlan(0x8100),
245 set_field(vlan_vid(4096 + 4000)),
246 output(ofp.OFPP_CONTROLLER)
247 ]
248 )
Zsolt Harasztia133a452016-12-22 01:26:57 -0800249 )
Zsolt Harasztid036b7e2016-12-23 15:36:01 -0800250 stub = voltha_pb2.VolthaLocalServiceStub(self.get_channel())
251 res = stub.UpdateLogicalDeviceFlowTable(update)
252 print 'success', res
253
254 def do_send_simulated_upstream_eapol(self, arg):
255 """
256 Send an EAPOL upstream from a simulated OLT
257 """
258 device_id = arg or self.default_device_id
259 requests.get('http://{}/devices/{}/test_eapol_in'.format(
260 self.voltha_sim_rest, device_id
261 ))
262
263 def do_inject_eapol_start(self, arg):
264 """
265 Send out an an EAPOL start message into the given Unix interface
266 """
267 pass
Zsolt Harasztia133a452016-12-22 01:26:57 -0800268
269
270if __name__ == '__main__':
271 c = VolthaCli()
272 c.load_history()
273 c.cmdloop()
274 c.save_history()