blob: 49a2bf91f6eefa492cec80e4f5bf19c80f307dd2 [file] [log] [blame]
Zack Williams41513bf2018-07-07 20:08:35 -07001# Copyright 2017-present Open Networking Foundation
2#
3# Licensed under the Apache License, Version 2.0 (the "License");
4# you may not use this file except in compliance with the License.
5# You may obtain a copy of the License at
6#
7# http://www.apache.org/licenses/LICENSE-2.0
8#
9# Unless required by applicable law or agreed to in writing, software
10# distributed under the License is distributed on an "AS IS" BASIS,
11# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12# See the License for the specific language governing permissions and
13# limitations under the License.
Chip Boling8be83842017-09-18 18:06:37 -050014
15from unittest import main
16
17from mock import Mock
18
19from tests.utests.voltha.core.flow_helpers import FlowHelpers
20from voltha.core import logical_device_agent
21from voltha.core.flow_decomposer import *
22from voltha.core.logical_device_agent import LogicalDeviceAgent
23from voltha.protos.device_pb2 import Device, Port
24from voltha.protos.logical_device_pb2 import LogicalDevice, LogicalPort
25from voltha.protos.openflow_13_pb2 import Flows, FlowGroups
26
27
28class test_multipon_logical_device_agent(FlowHelpers):
29
30 def setup_mock_registry(self):
31 registry = Mock()
32 logical_device_agent.registry = registry
33
34 def setUp(self):
35 self.setup_mock_registry()
36
37 self.flows = Flows(items=[])
38 self.groups = FlowGroups(items=[])
39 self.ld_ports = [
40 LogicalPort(
41 id='0',
42 device_id='olt',
43 device_port_no=0,
44 root_port=True,
45 ofp_port=ofp.ofp_port(port_no=0)
46 ),
47 LogicalPort(
48 id='101',
49 device_id='onu1',
50 device_port_no=0,
51 ofp_port=ofp.ofp_port(port_no=101)
52 ),
53 LogicalPort(
54 id='201',
55 device_id='onu2',
56 device_port_no=0,
57 ofp_port=ofp.ofp_port(port_no=201)
58 )
59 ]
60
61 self.devices = {
62 'olt': Device(
63 id='olt', root=True, parent_id='id'),
64 'onu1': Device(
65 id='onu1', parent_id='olt', parent_port_no=1, vlan=101),
66 'onu2': Device(
67 id='onu2', parent_id='olt', parent_port_no=2, vlan=201),
68 }
69
70 self.ports = {
71 'olt': [
72 Port(port_no=0, type=Port.ETHERNET_NNI, device_id='olt'),
73 Port(port_no=1, type=Port.PON_OLT, device_id='olt',
74 peers=[Port.PeerPort(device_id='onu1', port_no=1)]),
75 Port(port_no=2, type=Port.PON_OLT, device_id='olt',
76 peers=[Port.PeerPort(device_id='onu2', port_no=1)])
77 ],
78 'onu1': [
79 Port(port_no=0, type=Port.ETHERNET_UNI, device_id='onu1'),
80 Port(port_no=1, type=Port.PON_ONU, device_id='onu1',
81 peers=[Port.PeerPort(device_id='olt', port_no=1)])
82 ],
83 'onu2': [
84 Port(port_no=0, type=Port.ETHERNET_UNI, device_id='onu2'),
85 Port(port_no=1, type=Port.PON_ONU, device_id='onu2',
86 peers=[Port.PeerPort(device_id='olt', port_no=2)])
87 ],
88 }
89
90 self.device_flows = {
91 'olt': Flows(),
92 'onu1': Flows(),
93 'onu2': Flows()
94 }
95
96 self.device_groups = {
97 'olt': FlowGroups(),
98 'onu1': FlowGroups(),
99 'onu2': FlowGroups()
100 }
101
102 self.ld = LogicalDevice(id='id', root_device_id='olt')
103
104 self.root_proxy = Mock()
105 def get_devices(path):
106 if path == '':
107 return self.devices.values()
108 if path.endswith('/ports'):
109 return self.ports[path[:-len('/ports')]]
110 elif path.find('/') == -1:
111 return self.devices[path]
112 else:
113 raise Exception(
114 'Nothing to yield for path /devices/{}'.format(path))
115 def update_devices(path, data):
116 if path.endswith('/flows'):
117 self.device_flows[path[:-len('/flows')]] = data
118 elif path.endswith('/flow_groups'):
119 self.device_groups[path[:-len('/flow_groups')]] = data
120 else:
121 raise NotImplementedError(
122 'not handling path /devices/{}'.format(path))
123
124 self.root_proxy.get = lambda p: \
125 get_devices(p[len('/devices/'):]) if p.startswith('/devices') \
126 else None
127 self.root_proxy.update = lambda p, d: \
128 update_devices(p[len('/devices/'):], d) \
129 if p.startswith('/devices') \
130 else None
131 self.ld_proxy = Mock()
132 self.ld_proxy.get = lambda p: \
133 self.ld_ports if p == '/ports' else (
134 self.ld if p == '/' else None
135 )
136
137 self.flows_proxy = Mock()
138 self.flows_proxy.get = lambda _: self.flows # always '/' path
139 def update_flows(_, flows): # always '/' path
140 self.flows = flows
141 self.flows_proxy.update = update_flows
142
143 self.groups_proxy = Mock()
144 self.groups_proxy.get = lambda _: self.groups # always '/' path
145 def update_groups(_, groups): # always '/' path
146 self.groups = groups
147 self.groups_proxy.update = update_groups
148
149 self.core = Mock()
150 self.core.get_proxy = lambda path: \
151 self.root_proxy if path == '/' else (
152 self.ld_proxy if path.endswith('id') else (
153 self.flows_proxy if path.endswith('flows') else
154 self.groups_proxy
155 )
156 )
157
158 self.lda = LogicalDeviceAgent(self.core, self.ld)
159
160 def test_init(self):
161 pass # really just tests the setUp method
162
163 # ~~~~~~~~~~~~~~~~~~~~ DEFAULT RULES AND ROUTES ~~~~~~~~~~~~~~~~~~~~~~~~~~~
164
165 def test_default_rules(self):
166 rules = self.lda.get_all_default_rules()
167 # no default olt downstream and no default group for each of 3 devs
168 self.assertEqual(len(rules['olt'][0]), 0)
169 self.assertEqual(len(rules['olt'][1]), 0)
170 self.assertEqual(len(rules['onu1'][0]), 3)
171 self.assertEqual(len(rules['onu1'][1]), 0)
172 self.assertEqual(len(rules['onu2'][0]), 3)
173 self.assertEqual(len(rules['onu2'][1]), 0)
174
175 def test_routes(self):
176 self.lda.get_all_default_rules() # this will prepare the _routes
177 routes = self.lda._routes
178 self.assertEqual(len(routes), 4)
179 self.assertEqual(set(routes.keys()),
180 set([(0, 101), (0, 201), (101, 0), (201, 0)]))
181
182 # verify all routes
183 route = routes[(0, 101)]
184 self.assertEqual(len(route), 2)
185 self.assertEqual(route[0].device, self.devices['olt'])
186 self.assertEqual(route[0].ingress_port, self.ports['olt'][0])
187 self.assertEqual(route[0].egress_port, self.ports['olt'][1])
188 self.assertEqual(route[1].device, self.devices['onu1'])
189 self.assertEqual(route[1].ingress_port, self.ports['onu1'][1])
190 self.assertEqual(route[1].egress_port, self.ports['onu1'][0])
191
192 route = routes[(0, 201)]
193 self.assertEqual(len(route), 2)
194 self.assertEqual(route[0].device, self.devices['olt'])
195 self.assertEqual(route[0].ingress_port, self.ports['olt'][0])
196 self.assertEqual(route[0].egress_port, self.ports['olt'][2])
197 self.assertEqual(route[1].device, self.devices['onu2'])
198 self.assertEqual(route[1].ingress_port, self.ports['onu2'][1])
199 self.assertEqual(route[1].egress_port, self.ports['onu2'][0])
200
201 route = routes[(101, 0)]
202 self.assertEqual(len(route), 2)
203 self.assertEqual(route[0].device, self.devices['onu1'])
204 self.assertEqual(route[0].ingress_port, self.ports['onu1'][0])
205 self.assertEqual(route[0].egress_port, self.ports['onu1'][1])
206 self.assertEqual(route[1].device, self.devices['olt'])
207 self.assertEqual(route[1].ingress_port, self.ports['olt'][1])
208 self.assertEqual(route[1].egress_port, self.ports['olt'][0])
209
210 route = routes[(201, 0)]
211 self.assertEqual(len(route), 2)
212 self.assertEqual(route[0].device, self.devices['onu2'])
213 self.assertEqual(route[0].ingress_port, self.ports['onu2'][0])
214 self.assertEqual(route[0].egress_port, self.ports['onu2'][1])
215 self.assertEqual(route[1].device, self.devices['olt'])
216 self.assertEqual(route[1].ingress_port, self.ports['olt'][2])
217 self.assertEqual(route[1].egress_port, self.ports['olt'][0])
218
219 # ~~~~~~~~~~~~~~~~~~~~~~~~~~ FLOW DECOMP TESTS ~~~~~~~~~~~~~~~~~~~~~~~~~~~~
220
221 def test_eapol_flow_decomp_case(self):
222 self.lda.update_flow_table(mk_simple_flow_mod(
223 priority=1000,
224 match_fields=[in_port(201), eth_type(0x888e)],
Gamze Abaka53cc0a22019-01-31 12:06:11 +0000225 actions=[output(ofp.OFPP_CONTROLLER)],
226 meter_id=1,
227 metadata=32
Chip Boling8be83842017-09-18 18:06:37 -0500228 ))
229 self.lda._flow_table_updated(self.flows)
Gamze Abaka53cc0a22019-01-31 12:06:11 +0000230 self.assertEqual(len(self.device_flows['olt'].items), 1)
Chip Boling8be83842017-09-18 18:06:37 -0500231 self.assertEqual(len(self.device_flows['onu1'].items), 3)
Jonathan Hart5b435642018-08-20 08:50:05 -0700232 self.assertEqual(len(self.device_flows['onu2'].items), 3)
Chip Boling8be83842017-09-18 18:06:37 -0500233 self.assertEqual(len(self.device_groups['olt'].items), 0)
234 self.assertEqual(len(self.device_groups['onu1'].items), 0)
235 self.assertEqual(len(self.device_groups['onu2'].items), 0)
236
237 self.assertFlowsEqual(self.device_flows['olt'].items[0], mk_flow_stat(
238 priority=1000,
Gamze Abaka53cc0a22019-01-31 12:06:11 +0000239 match_fields=[in_port(2), eth_type(0x888e)],
Chip Boling8be83842017-09-18 18:06:37 -0500240 actions=[
Gamze Abaka53cc0a22019-01-31 12:06:11 +0000241 output(2147483645)
242 ],
243 meter_id=1,
244 metadata=32
Chip Boling8be83842017-09-18 18:06:37 -0500245 ))
246
247 def test_multicast_group_with_one_subscriber(self):
248 self.lda.update_group_table(mk_multicast_group_mod(
249 group_id=2,
250 buckets=[
251 ofp.ofp_bucket(actions=[
252 pop_vlan(),
253 output(201)
254 ]),
255 ]
256 ))
257 self.lda.update_flow_table(mk_simple_flow_mod(
258 priority=1000,
259 match_fields=[
260 in_port(0),
261 eth_type(0x800),
262 vlan_vid(4096 + 140),
263 ipv4_dst(0xe60a0a0a)
264 ],
265 actions=[group(2)]
266 ))
267 self.lda._flow_table_updated(self.flows)
268 self.assertEqual(len(self.device_flows['olt'].items), 1)
269 self.assertEqual(len(self.device_flows['onu1'].items), 3)
270 self.assertEqual(len(self.device_flows['onu2'].items), 4)
271 self.assertEqual(len(self.device_groups['olt'].items), 0)
272 self.assertEqual(len(self.device_groups['onu1'].items), 0)
273 self.assertEqual(len(self.device_groups['onu2'].items), 0)
274
275 self.assertFlowsEqual(self.device_flows['olt'].items[0], mk_flow_stat(
276 priority=1000,
277 match_fields=[in_port(0), eth_type(0x800), vlan_vid(4096 + 140),
278 ipv4_dst(0xe60a0a0a)],
279 actions=[
280 pop_vlan(),
Dimitris Benise95cf032018-08-30 00:27:51 +0300281 output(1)
Chip Boling8be83842017-09-18 18:06:37 -0500282 ]
283 ))
284 self.assertFlowsEqual(self.device_flows['onu2'].items[3], mk_flow_stat(
285 priority=1000,
286 match_fields=[in_port(1), eth_type(0x800), ipv4_dst(0xe60a0a0a)],
287 actions=[
288 output(0)
289 ]
290 ))
291
292 def test_multicast_group_with_no_subscribers(self):
293 self.lda.update_group_table(mk_multicast_group_mod(
294 group_id=2,
295 buckets=[] # No subscribers
296 ))
297 self.lda.update_flow_table(mk_simple_flow_mod(
298 priority=1000,
299 match_fields=[
300 in_port(0),
301 eth_type(0x800),
302 vlan_vid(4096 + 140),
303 ipv4_dst(0xe60a0a0a)
304 ],
305 actions=[group(2)]
306 ))
307 self.lda._flow_table_updated(self.flows)
Dimitris Benise95cf032018-08-30 00:27:51 +0300308 self.assertEqual(len(self.device_flows['olt'].items), 0)
Chip Boling8be83842017-09-18 18:06:37 -0500309 self.assertEqual(len(self.device_flows['onu1'].items), 3)
310 self.assertEqual(len(self.device_flows['onu2'].items), 3)
311 self.assertEqual(len(self.device_flows['onu2'].items), 3)
312 self.assertEqual(len(self.device_groups['olt'].items), 0)
313 self.assertEqual(len(self.device_groups['onu1'].items), 0)
314 self.assertEqual(len(self.device_groups['onu2'].items), 0)
315
Dimitris Benise95cf032018-08-30 00:27:51 +0300316 self.assertFlowNotInFlows(mk_flow_stat(
Chip Boling8be83842017-09-18 18:06:37 -0500317 priority=1000,
318 match_fields=[in_port(0), eth_type(0x800), vlan_vid(4096 + 140),
319 ipv4_dst(0xe60a0a0a)],
320 actions=[
321 pop_vlan(),
322 output(2)
323 ]
Dimitris Benise95cf032018-08-30 00:27:51 +0300324 ), self.device_flows['olt'])
Chip Boling8be83842017-09-18 18:06:37 -0500325 # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~ COMPLEX TESTS ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
326
327 def test_complex_flow_table_decomposition(self):
328
329 # Various controller-bound rules
330 for _in_port in (101, 201):
331 self.lda.update_flow_table(mk_simple_flow_mod(
332 priority=2000,
333 match_fields=[in_port(_in_port), eth_type(0x888e)],
334 actions=[
335 push_vlan(0x8100),
336 set_field(vlan_vid(4096 + 4000)),
337 output(ofp.OFPP_CONTROLLER)
338 ]
339 ))
340 self.lda.update_flow_table(mk_simple_flow_mod(
341 priority=1000,
342 match_fields=[in_port(_in_port), eth_type(0x800), ip_proto(2)],
343 actions=[output(ofp.OFPP_CONTROLLER)]
344 ))
345 self.lda.update_flow_table(mk_simple_flow_mod(
346 priority=1000,
347 match_fields=[in_port(_in_port), eth_type(0x800), ip_proto(17),
348 udp_src(68), udp_dst(67)],
349 actions=[output(ofp.OFPP_CONTROLLER)]
350 ))
351
352 # Multicast channels
353 mcast_setup = (
354 (1, 0xe4010101, ()),
355 (2, 0xe4010102, (101,)),
356 (3, 0xe4010103, (201,)),
357 (4, 0xe4010104, (101, 201)),
358 )
359 for group_id, mcast_addr, ports in mcast_setup:
360 self.lda.update_group_table(mk_multicast_group_mod(
361 group_id=group_id,
362 buckets=[
363 ofp.ofp_bucket(actions=[
364 pop_vlan(),
365 output(port)
366 ]) for port in ports
367 ]))
368 self.lda.update_flow_table(mk_simple_flow_mod(
369 priority=1000,
370 match_fields=[
371 in_port(0),
372 eth_type(0x800),
373 vlan_vid(4096 + 140),
374 ipv4_dst(mcast_addr)
375 ],
376 actions=[
377 group(group_id)
378 ]
379 ))
380
381 # Unicast channels for each subscriber
382 for port, c_vid in ((101, 101), (201, 201)):
383
384 # Downstream flow 1 for nni to pon
385 self.lda.update_flow_table(mk_simple_flow_mod(
386 priority=500,
387 match_fields=[
388 in_port(0),
Gamze Abaka53cc0a22019-01-31 12:06:11 +0000389 vlan_vid(4096 + 1000)
Chip Boling8be83842017-09-18 18:06:37 -0500390 ],
391 actions=[pop_vlan()],
392 next_table_id=1
393 ))
394
395 # Downstream flow 2
396 self.lda.update_flow_table(mk_simple_flow_mod(
397 priority=500,
398 match_fields=[in_port(0), vlan_vid(4096 + c_vid)],
399 actions=[set_field(vlan_vid(4096 + 0)), output(port)]
400 ))
401
402 # upstream flow 1 for the 0-tagged case
403 self.lda.update_flow_table(mk_simple_flow_mod(
404 priority=500,
405 match_fields=[in_port(port), vlan_vid(4096 + 0)],
406 actions=[set_field(vlan_vid(4096 + c_vid))],
407 next_table_id=1
408 ))
409 # ... and for the untagged case
410 self.lda.update_flow_table(mk_simple_flow_mod(
411 priority=500,
412 match_fields=[in_port(port), vlan_vid(0)],
413 actions=[push_vlan(0x8100), set_field(vlan_vid(4096 + c_vid))],
414 next_table_id=1
415 ))
416
417 # Upstream flow 2 for s-tag
418 self.lda.update_flow_table(mk_simple_flow_mod(
419 priority=500,
420 match_fields=[in_port(port), vlan_vid(4096 + c_vid)],
421 actions=[
422 push_vlan(0x8100),
423 set_field(vlan_vid(4096 + 1000)),
424 output(0)
425 ]
426 ))
427
Gamze Abaka53cc0a22019-01-31 12:06:11 +0000428 self.assertEqual(len(self.flows.items), 19)
Chip Boling8be83842017-09-18 18:06:37 -0500429 self.assertEqual(len(self.groups.items), 4)
430
431 # trigger flow table decomposition
432 self.lda._flow_table_updated(self.flows)
433
434 # now check device level flows
Gamze Abaka53cc0a22019-01-31 12:06:11 +0000435 self.assertEqual(len(self.device_flows['olt'].items), 10)
Jonathan Hart5b435642018-08-20 08:50:05 -0700436 self.assertEqual(len(self.device_flows['onu1'].items), 5)
437 self.assertEqual(len(self.device_flows['onu2'].items), 5)
Chip Boling8be83842017-09-18 18:06:37 -0500438 self.assertEqual(len(self.device_groups['olt'].items), 0)
439 self.assertEqual(len(self.device_groups['onu1'].items), 0)
440 self.assertEqual(len(self.device_groups['onu2'].items), 0)
441
442 # Flows installed on the OLT
Chip Boling8be83842017-09-18 18:06:37 -0500443 self.assertFlowsEqual(self.device_flows['olt'].items[0], mk_flow_stat(
444 priority=2000,
Gamze Abaka53cc0a22019-01-31 12:06:11 +0000445 match_fields=[in_port(1), eth_type(0x888e)],
Chip Boling8be83842017-09-18 18:06:37 -0500446 actions=[push_vlan(0x8100), set_field(vlan_vid(4096 + 4000)),
Gamze Abaka53cc0a22019-01-31 12:06:11 +0000447 output(2147483645)]
448 ))
449 self.assertFlowsEqual(self.device_flows['olt'].items[1], mk_flow_stat(
450 priority=1000,
451 match_fields=[in_port(1), eth_type(0x800), ip_proto(2)],
452 actions=[output(2147483645)]
Chip Boling8be83842017-09-18 18:06:37 -0500453 ))
454 self.assertFlowsEqual(self.device_flows['olt'].items[2], mk_flow_stat(
455 priority=1000,
Gamze Abaka53cc0a22019-01-31 12:06:11 +0000456 match_fields=[in_port(1), eth_type(0x800), ip_proto(17),
457 udp_src(68), udp_dst(67)],
458 actions=[output(2147483645)]
Chip Boling8be83842017-09-18 18:06:37 -0500459 ))
460 self.assertFlowsEqual(self.device_flows['olt'].items[3], mk_flow_stat(
Gamze Abaka53cc0a22019-01-31 12:06:11 +0000461 priority=2000,
462 match_fields=[in_port(2), eth_type(0x888e)],
463 actions=[push_vlan(0x8100), set_field(vlan_vid(4096 + 4000)),
464 output(2147483645)]
Chip Boling8be83842017-09-18 18:06:37 -0500465 ))
466 self.assertFlowsEqual(self.device_flows['olt'].items[4], mk_flow_stat(
467 priority=1000,
Gamze Abaka53cc0a22019-01-31 12:06:11 +0000468 match_fields=[in_port(2), eth_type(0x800), ip_proto(2)],
469 actions=[output(2147483645)]
Chip Boling8be83842017-09-18 18:06:37 -0500470 ))
471 self.assertFlowsEqual(self.device_flows['olt'].items[5], mk_flow_stat(
Gamze Abaka53cc0a22019-01-31 12:06:11 +0000472 priority=1000,
473 match_fields=[in_port(2), eth_type(0x800), ip_proto(17),
474 udp_src(68), udp_dst(67)],
475 actions=[output(2147483645)]
Chip Boling8be83842017-09-18 18:06:37 -0500476 ))
477 self.assertFlowsEqual(self.device_flows['olt'].items[6], mk_flow_stat(
Chip Boling8be83842017-09-18 18:06:37 -0500478 priority=1000,
Gamze Abaka53cc0a22019-01-31 12:06:11 +0000479 match_fields=[in_port(0), eth_type(0x800), vlan_vid(4096 + 140),
480 ipv4_dst(0xE4010102)],
481 actions=[pop_vlan(), output(1)]
Chip Boling8be83842017-09-18 18:06:37 -0500482 ))
483 self.assertFlowsEqual(self.device_flows['olt'].items[9], mk_flow_stat(
Gamze Abaka53cc0a22019-01-31 12:06:11 +0000484 priority=500,
485 match_fields=[in_port(0), vlan_vid(4096 + 1000)],
Chip Boling8be83842017-09-18 18:06:37 -0500486 actions=[pop_vlan(), output(2)]
Chip Boling8be83842017-09-18 18:06:37 -0500487 ))
488
489 # Flows installed on the ONU1
490 self.assertFlowsEqual(self.device_flows['onu1'].items[0], mk_flow_stat(
491 priority=500,
492 match_fields=[in_port(0), vlan_vid(4096 + 0)],
493 actions=[
494 set_field(vlan_vid(4096 + 101)), output(1)]
495 ))
Chip Boling8be83842017-09-18 18:06:37 -0500496 self.assertFlowsEqual(self.device_flows['onu1'].items[2], mk_flow_stat(
497 priority=500,
498 match_fields=[in_port(1), vlan_vid(4096 + 101)],
499 actions=[set_field(vlan_vid(4096 + 0)), output(0)]
500 ))
501 self.assertFlowsEqual(self.device_flows['onu1'].items[1], mk_flow_stat(
502 priority=500,
503 match_fields=[in_port(0), vlan_vid(0)],
504 actions=[push_vlan(0x8100), set_field(vlan_vid(4096 + 101)),
505 output(1)]
506 ))
507
508 # Flows installed on the ONU2
509 self.assertFlowsEqual(self.device_flows['onu2'].items[0], mk_flow_stat(
510 priority=500,
511 match_fields=[in_port(0), vlan_vid(4096 + 0)],
512 actions=[
513 set_field(vlan_vid(4096 + 201)), output(1)]
514 ))
Chip Boling8be83842017-09-18 18:06:37 -0500515 self.assertFlowsEqual(self.device_flows['onu2'].items[2], mk_flow_stat(
516 priority=500,
517 match_fields=[in_port(1), vlan_vid(4096 + 201)],
518 actions=[set_field(vlan_vid(4096 + 0)), output(0)]
519 ))
520 self.assertFlowsEqual(self.device_flows['onu2'].items[1], mk_flow_stat(
521 priority=500,
522 match_fields=[in_port(0), vlan_vid(0)],
523 actions=[push_vlan(0x8100), set_field(vlan_vid(4096 + 201)),
524 output(1)]
525 ))
526
527
528if __name__ == '__main__':
529 main()