blob: 5c8d2778c522fd591952829701011aab9b254d15 [file] [log] [blame]
Jonghwan Hyun812c70f2018-02-16 16:33:16 -08001"""
2Copyright 2017 Open Networking Foundation ( ONF )
3
4Please refer questions to either the onos test mailing list at <onos-test@onosproject.org>,
5the System Testing Plans and Results wiki page at <https://wiki.onosproject.org/x/voMg>,
6or the System Testing Guide page at <https://wiki.onosproject.org/x/WYQg>
7
8 TestON is free software: you can redistribute it and/or modify
9 it under the terms of the GNU General Public License as published by
10 the Free Software Foundation, either version 2 of the License, or
11 ( at your option ) any later version.
12
13 TestON is distributed in the hope that it will be useful,
14 but WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 GNU General Public License for more details.
17
18 You should have received a copy of the GNU General Public License
19 along with TestON. If not, see <http://www.gnu.org/licenses/>.
20"""
21
You Wang68568b12019-03-04 11:49:57 -080022import tests.USECASE.SegmentRouting.dependencies.cfgtranslator as translator
23
Jonghwan Hyun812c70f2018-02-16 16:33:16 -080024class SRDynamicConfTest:
25 def __init__( self ):
26 self.default = ''
27
28 @staticmethod
29 def runTest( main, testIndex, topology, onosNodes, description, vlan=( 0, 0, 0, 0 ) ):
30 '''
31 Tests connectivity for each test case.
32 Configuration files:
33 - (0x1, 0x2, 2x2, 2x4).json: device configuration, fed to ONOS before configuration change
34 - CASE*.json: interface configuration, fed to ONOS before configuration change
35 - CASE*0.chart: ping chart, used to check connectivity before configuration change.
36 Shared among same test scenario with different topology.
37 - CASE*0_after.chart: ping chart, used to check connectivity after configuration change.
38 Shared among same test scenario with different topology.
39 Only used when ping chart is updated.
40 '''
Jonghwan Hyun812c70f2018-02-16 16:33:16 -080041 try:
Jon Hall9b0de1f2020-08-24 15:38:04 -070042 topo = dict()
43 # (number of spine switch, number of leaf switch, dual-homed, description, port number of h1)
44 topo[ '0x1' ] = ( 0, 1, False, 'single ToR', 1 )
45 topo[ '0x2' ] = ( 0, 2, True, 'dual-homed ToR', 2 )
46 topo[ '2x2' ] = ( 2, 2, False, '2x2 leaf-spine topology', 3 )
47 topo[ '2x4' ] = ( 2, 4, True, '2x4 dual-homed leaf-spine topology', 6 )
48 fanout = 4
49 switchNames = {}
50 switchNames[ '2x2' ] = [ "leaf1", "leaf2", "spine101", "spine102" ]
51
52 TAG = 'CASE%d' % testIndex
53 skipPackage = False
54 init = False
55 dualHomed = topo[ topology ][ 2 ]
56 portNum = topo[ topology ][ 4 ]
57 defaultIntf = 'bond0' if dualHomed else 'eth0'
58
59 from tests.USECASE.SegmentRouting.dependencies.Testcaselib import Testcaselib as run
60 if not hasattr( main, 'apps' ):
61 init = True
62 run.initTest( main )
63 # Skip onos packaging if the clusrer size stays the same
64 if not init and onosNodes == main.Cluster.numCtrls:
65 skipPackage = True
66
67 main.case( '%s, with %s and %d ONOS instance%s' %
68 ( description, topo[ topology ][ 3 ], onosNodes, 's' if onosNodes > 1 else '' ) )
69 main.cfgName = topology
70 main.Cluster.setRunningNode( onosNodes )
71 run.installOnos( main, skipPackage=skipPackage, cliSleep=5 )
72
73 # Provide common configuration
74 # TODO: Generate json and chart dynamically, according to topologies and scenarios
You Wang68568b12019-03-04 11:49:57 -080075 if main.useBmv2:
76 # Translate configuration file from OVS-OFDPA to BMv2 driver
Jon Hall9b0de1f2020-08-24 15:38:04 -070077 translator.bmv2ToOfdpa( main ) # Try to cleanup if switching between switch types
Jon Hall43060f62020-06-23 13:13:33 -070078 switchPrefix = main.params[ 'DEPENDENCY' ].get( 'switchPrefix', "bmv2" )
Jon Hall9b0de1f2020-08-24 15:38:04 -070079 translator.ofdpaToBmv2( main, switchPrefix=switchPrefix )
You Wang68568b12019-03-04 11:49:57 -080080 else:
Jon Hall9b0de1f2020-08-24 15:38:04 -070081 translator.bmv2ToOfdpa( main )
82 run.loadJson( main )
83 run.loadChart( main )
84
85 # Provide topology-specific interface configuration
86 import json
87 try:
88 intfCfg = "%s%s%s.json" % ( main.configPath, main.forJson, TAG )
89 if main.useBmv2:
90 # Translate configuration file from OVS-OFDPA to BMv2 driver
Jon Hallf69e3162020-09-01 09:08:44 -070091 translator.bmv2ToOfdpa( main, intfCfg ) # Try to cleanup if switching between switch types
Jon Hall9b0de1f2020-08-24 15:38:04 -070092 switchPrefix = main.params[ 'DEPENDENCY' ].get( 'switchPrefix', "bmv2" )
93 translator.ofdpaToBmv2( main, switchPrefix=switchPrefix, cfgFile=intfCfg )
94 else:
95 translator.bmv2ToOfdpa( main, intfCfg )
96 with open( intfCfg ) as cfg:
97 main.Cluster.active( 0 ).REST.setNetCfg( json.load( cfg ) )
98 except IOError:
99 # Load default interface configuration
100 defaultIntfCfg = "%s%s%s_ports.json" % ( main.configPath, main.forJson, topology )
101 if main.useBmv2:
102 # Translate configuration file from OVS-OFDPA to BMv2 driver
Jon Hallf69e3162020-09-01 09:08:44 -0700103 translator.bmv2ToOfdpa( main, defaultIntfCfg ) # Try to cleanup if switching between switch types
Jon Hall9b0de1f2020-08-24 15:38:04 -0700104 switchPrefix = main.params[ 'DEPENDENCY' ].get( 'switchPrefix', "bmv2" )
105 translator.ofdpaToBmv2( main, switchPrefix=switchPrefix, cfgFile=defaultIntfCfg )
106 else:
107 translator.bmv2ToOfdpa( main, defaultIntfCfg )
108 with open( defaultIntfCfg ) as cfg:
109 main.Cluster.active( 0 ).REST.setNetCfg( json.load( cfg ) )
110
111 try:
112 with open( "%s%sCASE%d.chart" % (main.configPath, main.forChart, testIndex / 10 * 10) ) as chart:
113 main.pingChart = json.load( chart )
114 except IOError:
115 # Load default chart
116 with open( "%s%sdefault.chart" % (main.configPath, main.forChart) ) as chart:
117 main.pingChart = json.load( chart )
118
119 # Set up topology
120 if hasattr( main, 'Mininet1' ):
Jon Hallf69e3162020-09-01 09:08:44 -0700121 run.mnDockerSetup( main )
Jon Hall9b0de1f2020-08-24 15:38:04 -0700122 # Run the test with mininet topology
123 mininet_args = ' --spine=%d --leaf=%d --fanout=%d' \
124 % ( topo[ topology ][ 0 ], topo[ topology ][ 1 ], fanout )
125 if len( vlan ) > 0 :
126 mininet_args += ' --vlan=%s' % ( ','.join( [ '%d' % vlanId for vlanId in vlan ] ) )
127 if topo[ topology ][ 0 ] > 0:
128 mininet_args += ',0,0,0,0'
129 if dualHomed:
130 mininet_args += ' --dual-homed'
131 if main.useBmv2:
Jon Hallf69e3162020-09-01 09:08:44 -0700132 mininet_args += ' --switch %s' % main.switchType
133 main.log.info( "Using %s switch" % main.switchType )
Jon Hall9b0de1f2020-08-24 15:38:04 -0700134
135 run.startMininet( main, 'trellis_fabric.py', args=mininet_args )
136 else:
137 # Run the test with physical devices
138 run.connectToPhysicalNetwork( main, switchNames[ topology ] )
139
140 # minFlowCountPerLeaf = 13 + [# of ports] * 5 + [# of hosts] * 2 + [# of vlan ids]
141 minFlowCountPerLeaf = 13 + ( fanout + topo[ topology ][ 0 ]) * 5 + fanout * 2 + len( set( vlan ) )
142 run.checkFlows( main, minFlowCount=minFlowCountPerLeaf * topo[ topology ][ 1 ], sleep=5, dumpflows=False )
143 # Check connectivity before changing interface configuration
144 run.pingAll( main, '%s_Before' % TAG, retryAttempts=2 )
145
You Wang68568b12019-03-04 11:49:57 -0800146 if main.useBmv2:
Jon Hall9b0de1f2020-08-24 15:38:04 -0700147 leaf_dpid = [ "device:bmv2:leaf%d" % ( ls + 1 ) for ls in range( topo[ topology ][ 1 ] ) ]
You Wang68568b12019-03-04 11:49:57 -0800148 else:
Jon Hall9b0de1f2020-08-24 15:38:04 -0700149 leaf_dpid = [ "of:%016d" % ( ls + 1 ) for ls in range( topo[ topology ][ 1 ] ) ]
150 for dpid in leaf_dpid:
151 run.checkFlowsByDpid( main, dpid, minFlowCountPerLeaf, sleep=5 )
Jonghwan Hyun812c70f2018-02-16 16:33:16 -0800152
Jon Hall9b0de1f2020-08-24 15:38:04 -0700153 # Testcase-specific interface configuration change
154 if testIndex / 10 == 1:
155 # CASE11-14
156 if hasattr( main, 'Mininet1' ):
157 # Assign vlan tag 10 to host h1
158 main.Mininet1.assignVLAN( 'h1', 'h1-%s' % defaultIntf, '10' )
159 # Update port configuration of port 1
160 SRDynamicConfTest.updateIntfCfg( main, portNum, dualHomed,
161 [ '10.0.2.254/24', ], tagged=[ 10, ] )
162 else:
163 # TODO: update physical device configuration, same for all test cases
164 pass
165 elif testIndex / 10 == 2:
166 # CASE21-24
167 if hasattr( main, 'Mininet1' ):
168 # Update port configuration of port 1
169 SRDynamicConfTest.updateIntfCfg( main, portNum, dualHomed,
170 [ '10.0.2.254/24', ], untagged=20 )
171 elif testIndex / 10 == 3:
172 # CASE31-34
173 if hasattr( main, 'Mininet1' ):
174 # Update port configuration of port 1
175 SRDynamicConfTest.updateIntfCfg( main, portNum, dualHomed,
176 [ '10.0.2.254/24', ], untagged=110 )
177 # Update port configuration of port 2
178 SRDynamicConfTest.updateIntfCfg( main, portNum + 1, dualHomed,
179 [ '10.0.2.254/24', ], untagged=110 )
180 elif testIndex / 10 == 4:
181 # CASE41-44
182 if hasattr( main, 'Mininet1' ):
183 # Assign vlan tag 20 to host h1
184 main.Mininet1.assignVLAN( 'h1', 'h1-%s' % defaultIntf, '20')
185 # Update port configuration of port 1
186 SRDynamicConfTest.updateIntfCfg( main, portNum, dualHomed,
187 [ '10.0.2.254/24', ], tagged=[ 20, ] )
188 elif testIndex / 10 == 5:
189 # CASE51-54
190 if hasattr( main, 'Mininet1' ):
191 # Update port configuration of port 1
192 SRDynamicConfTest.updateIntfCfg( main, portNum, dualHomed,
193 [ '10.0.2.254/24', ], tagged=[ 20, ], native=10 )
194 elif testIndex / 10 == 6:
195 # CASE61-64
196 if hasattr( main, 'Mininet1' ):
197 # Update port configuration of port 1
198 SRDynamicConfTest.updateIntfCfg( main, portNum, dualHomed,
199 [ '10.0.2.254/24', ], tagged=[ 120, ], native=110 )
200 # Update port configuration of port 2
201 SRDynamicConfTest.updateIntfCfg( main, portNum + 1, dualHomed,
202 [ '10.0.2.254/24', ], tagged=[ 120, ], native=110 )
203 elif testIndex / 10 == 7:
204 # CASE71-74
205 if hasattr( main, 'Mininet1' ):
206 # Update host configuration of h1
207 main.Mininet1.removeVLAN( 'h1', 'h1-%s.10' % defaultIntf )
208 # Update port configuration of port 1
209 SRDynamicConfTest.updateIntfCfg( main, portNum, dualHomed,
210 [ '10.0.2.254/24', ], untagged=10 )
211 elif testIndex / 10 == 8:
212 # CASE81-84
213 if hasattr( main, 'Mininet1' ):
214 # Update port configuration of port 1
215 SRDynamicConfTest.updateIntfCfg( main, portNum, dualHomed,
216 [ '10.0.2.254/24', ], tagged=[ 20, ], native=10 )
217 elif testIndex / 10 == 9:
218 # CASE91-94
219 if hasattr( main, 'Mininet1' ):
220 # Update host configuration
221 main.Mininet1.removeVLAN( 'h1', 'h1-%s.10' % defaultIntf )
222 main.Mininet1.removeVLAN( 'h2', 'h2-%s.10' % defaultIntf )
Jonghwan Hyun812c70f2018-02-16 16:33:16 -0800223
Jon Hall9b0de1f2020-08-24 15:38:04 -0700224 # Update port configuration
225 SRDynamicConfTest.updateIntfCfg( main, portNum, dualHomed,
226 [ '10.0.2.254/24', ], tagged=[ 120, ], native=110 )
227 SRDynamicConfTest.updateIntfCfg( main, portNum + 1, dualHomed,
228 [ '10.0.2.254/24', ], tagged=[ 120, ], native=110 )
229 elif testIndex / 10 == 10:
230 # CASE101-104
231 if hasattr( main, 'Mininet1' ):
232 # Update port configuration
233 SRDynamicConfTest.updateIntfCfg( main, portNum, dualHomed,
234 [ '10.0.2.254/24', ], untagged=20 )
235 elif testIndex / 10 == 11:
236 # CASE111-114
237 if hasattr( main, 'Mininet1' ):
238 # Update port configuration
239 SRDynamicConfTest.updateIntfCfg( main, portNum, dualHomed,
240 [ '10.0.2.254/24', ], tagged=[ 20, ] )
241 elif testIndex / 10 == 12:
242 # CASE121-124
243 if hasattr( main, 'Mininet1' ):
244 # Update port configuration
245 SRDynamicConfTest.updateIntfCfg( main, portNum, dualHomed,
246 [ '10.0.2.254/24', ], tagged=[ 20, ], native=110 )
247 SRDynamicConfTest.updateIntfCfg( main, portNum + 1, dualHomed,
248 [ '10.0.2.254/24', ], tagged=[ 20, ], native=110 )
249 elif testIndex / 10 == 13:
250 # CASE131-134
251 if hasattr( main, 'Mininet1' ):
252 # Update port configuration
253 SRDynamicConfTest.updateIntfCfg( main, portNum, dualHomed,
254 [ '10.0.2.254/24', ], tagged=[ 120, ], native=10 )
255 elif testIndex / 10 == 14:
256 # CASE141-144
257 if hasattr( main, 'Mininet1' ):
258 # Update port configuration
259 SRDynamicConfTest.updateIntfCfg( main, portNum, dualHomed,
260 [ '10.0.2.254/24', ], tagged=[ 20, ] )
261 elif testIndex / 10 == 15:
262 # CASE151-154
263 if hasattr( main, 'Mininet1' ):
264 # Update port configuration
265 SRDynamicConfTest.updateIntfCfg( main, portNum, dualHomed,
266 [ '10.0.2.254/24', ], tagged=[ 120, ] )
267 elif testIndex / 10 == 16:
268 # CASE161-164
269 if hasattr( main, 'Mininet1' ):
270 # Update port configuration
271 SRDynamicConfTest.updateIntfCfg( main, portNum, dualHomed,
272 [ '10.0.2.254/24', ], tagged=[ 20, ], native=10 )
273 elif testIndex / 10 == 17:
274 # CASE171-174
275 if hasattr( main, 'Mininet1' ):
276 # Update port configuration
277 SRDynamicConfTest.updateIntfCfg( main, portNum, dualHomed,
278 [ '10.0.2.254/24', ], tagged=[ 120, ] )
279 elif testIndex / 10 == 18:
280 # CASE181-184
281 if hasattr( main, 'Mininet1' ):
282 # Update port configuration
283 SRDynamicConfTest.updateIntfCfg( main, portNum, dualHomed,
284 [ '10.0.2.254/24', ], tagged=[ 20, ], native=10 )
285 elif testIndex / 10 == 19:
286 # CASE191-194
287 if hasattr( main, 'Mininet1' ):
288 # Update port configuration
289 SRDynamicConfTest.updateIntfCfg( main, portNum, dualHomed,
290 [ '10.0.2.254/24', ], untagged=20 )
291 elif testIndex / 10 == 20:
292 # CASE201-204
293 if hasattr( main, 'Mininet1' ):
294 # Update port configuration
295 SRDynamicConfTest.updateIntfCfg( main, portNum, dualHomed,
296 [ '10.0.2.254/24', ], tagged=[ 20 ] )
297 elif testIndex / 10 == 21:
298 # CASE211-214
299 if hasattr( main, 'Mininet1' ):
300 # Update port configuration
301 SRDynamicConfTest.updateIntfCfg( main, portNum, dualHomed,
302 [ '10.0.2.254/24', ], tagged=[ 20 ], native=110 )
303 elif testIndex / 10 == 22:
304 # CASE221-224
305 if hasattr( main, 'Mininet1' ):
306 # Update port configuration
307 SRDynamicConfTest.updateIntfCfg( main, portNum, dualHomed,
308 [ '10.0.2.254/24', ], tagged=[ 120 ], native=10 )
309 elif testIndex / 10 == 23:
310 # CASE231-234
311 if hasattr( main, "Mininet1" ):
312 SRDynamicConfTest.updateIntfCfg( main, portNum, dualHomed,
313 [ '10.0.2.254/24', ], tagged=[ 10, ] )
314 for dpid in leaf_dpid:
315 run.checkFlowsByDpid( main, dpid, minFlowCountPerLeaf, sleep=5 )
316 main.pingChart[ 'leaf1' ][ 'expect' ] = False
317 run.pingAll( main, '%s_1' % TAG, retryAttempts=2 )
Jonghwan Hyun812c70f2018-02-16 16:33:16 -0800318
Jon Hall9b0de1f2020-08-24 15:38:04 -0700319 SRDynamicConfTest.updateIntfCfg( main, portNum, dualHomed,
320 [ '10.0.2.254/24', ], untagged=50 )
321 for dpid in leaf_dpid:
322 run.checkFlowsByDpid( main, dpid, minFlowCountPerLeaf, sleep=5 )
323 run.pingAll( main, '%s_2' % TAG, retryAttempts=2 )
Jonghwan Hyun812c70f2018-02-16 16:33:16 -0800324
Jon Hall9b0de1f2020-08-24 15:38:04 -0700325 SRDynamicConfTest.updateIntfCfg( main, portNum, dualHomed,
326 [ '10.0.2.254/24', ], tagged=[ 20, ] )
327 for dpid in leaf_dpid:
328 run.checkFlowsByDpid( main, dpid, minFlowCountPerLeaf, sleep=5 )
329 run.pingAll( main, '%s_3' % TAG, retryAttempts=2 )
Jonghwan Hyun812c70f2018-02-16 16:33:16 -0800330
Jon Hall9b0de1f2020-08-24 15:38:04 -0700331 SRDynamicConfTest.updateIntfCfg( main, portNum, dualHomed,
332 [ '10.0.2.254/24', ], tagged=[ 40, ], native=10 )
333 for dpid in leaf_dpid:
334 run.checkFlowsByDpid( main, dpid, minFlowCountPerLeaf, sleep=5 )
335 main.pingChart[ 'leaf1' ][ 'expect' ] = True
336 run.pingAll( main, '%s_4' % TAG, retryAttempts=2 )
Jonghwan Hyun812c70f2018-02-16 16:33:16 -0800337
Jon Hall9b0de1f2020-08-24 15:38:04 -0700338 SRDynamicConfTest.updateIntfCfg( main, portNum, dualHomed,
339 [ '10.0.2.254/24', ], tagged=[ 20, ] )
340 for dpid in leaf_dpid:
341 run.checkFlowsByDpid( main, dpid, minFlowCountPerLeaf, sleep=5 )
342 main.pingChart[ 'leaf1' ][ 'expect' ] = False
343 run.pingAll( main, '%s_5' % TAG, retryAttempts=2 )
Jonghwan Hyun812c70f2018-02-16 16:33:16 -0800344
Jon Hall9b0de1f2020-08-24 15:38:04 -0700345 SRDynamicConfTest.updateIntfCfg( main, portNum, dualHomed,
346 [ '10.0.2.254/24', ], untagged= 20 )
347 for dpid in leaf_dpid:
348 run.checkFlowsByDpid( main, dpid, minFlowCountPerLeaf, sleep=5 )
349 run.pingAll( main, '%s_6' % TAG, retryAttempts=2 )
Jonghwan Hyun812c70f2018-02-16 16:33:16 -0800350
Jon Hall9b0de1f2020-08-24 15:38:04 -0700351 SRDynamicConfTest.updateIntfCfg( main, portNum, dualHomed,
352 [ '10.0.2.254/24', ], untagged= 10 )
353 for dpid in leaf_dpid:
354 run.checkFlowsByDpid( main, dpid, minFlowCountPerLeaf, sleep=5 )
355 main.pingChart[ 'leaf1' ][ 'expect' ] = True
356 elif testIndex / 10 == 24:
357 # CASE243-244
358 # Only for 2x2 and 2x4 topology, to test reachability from other leaf
359 if hasattr( main, "Mininet1" ):
360 # Update host IP and default GW
361 main.Mininet1.changeIP( 'h1', 'h1-%s' % defaultIntf, '10.0.6.1', '255.255.255.0' )
362 main.Mininet1.changeDefaultGateway( 'h1', '10.0.6.254' )
363 # Update port configuration
364 SRDynamicConfTest.updateIntfCfg( main, portNum, dualHomed,
365 [ '10.0.6.254/24', ], untagged=60 )
Jonghwan Hyun812c70f2018-02-16 16:33:16 -0800366
Jon Hall9b0de1f2020-08-24 15:38:04 -0700367 # Update ping chart in case it is changed
368 try:
369 with open( "%s%sCASE%d_after.chart" % (main.configPath, main.forChart, testIndex / 10 * 10 ) ) as chart:
370 main.pingChart = json.load(chart)
371 except IOError:
372 main.log.debug( "Ping chart is not changed" )
Jonghwan Hyun812c70f2018-02-16 16:33:16 -0800373
Jon Hall9b0de1f2020-08-24 15:38:04 -0700374 # Check connectivity after changing interface configuration
375 run.checkFlows( main, minFlowCount=minFlowCountPerLeaf * topo[ topology ][ 1 ], sleep=5, dumpflows=False )
376 run.pingAll( main, '%s_After' % TAG, retryAttempts=2 )
Jonghwan Hyun812c70f2018-02-16 16:33:16 -0800377
Jon Hall9b0de1f2020-08-24 15:38:04 -0700378 except Exception as e:
379 main.log.exception( "Error in runTest" )
380 main.skipCase( result="FAIL", msg=e )
Jon Hallf69e3162020-09-01 09:08:44 -0700381 finally:
382 run.cleanup( main )
Jonghwan Hyun812c70f2018-02-16 16:33:16 -0800383
384 @staticmethod
385 def updateIntfCfg( main, portNum, dualHomed, ips=[], untagged=0, tagged=[], native=0 ):
386 from tests.USECASE.SegmentRouting.dependencies.Testcaselib import Testcaselib as run
You Wang68568b12019-03-04 11:49:57 -0800387 if main.useBmv2:
388 run.updateIntfCfg( main, "device:bmv2:leaf1/%d" % portNum,
Jonghwan Hyun812c70f2018-02-16 16:33:16 -0800389 ips=ips, untagged=untagged, tagged=tagged, native=native )
You Wang68568b12019-03-04 11:49:57 -0800390 else:
391 run.updateIntfCfg( main, "of:0000000000000001/%d" % portNum,
392 ips=ips, untagged=untagged, tagged=tagged, native=native )
393 if dualHomed:
394 if main.useBmv2:
395 run.updateIntfCfg( main, "device:bmv2:leaf2/%d" % portNum,
396 ips=ips, untagged=untagged, tagged=tagged, native=native )
397 else:
398 run.updateIntfCfg( main, "of:0000000000000002/%d" % portNum,
399 ips=ips, untagged=untagged, tagged=tagged, native=native )