1 # Copyright 2016 Spirent Communications.
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
7 # http://www.apache.org/licenses/LICENSE-2.0
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.
16 @author Spirent Communications
18 This test automates the RFC2544 tests using the Spirent
19 TestCenter REST APIs. This test supports Python 3.4
27 logger = logging.getLogger(__name__)
31 """Create the directory as specified in path """
32 if not os.path.exists(path):
36 logger.error("Failed to create directory %s: %s", path, str(e))
40 def write_query_results_to_csv(results_path, csv_results_file_prefix,
42 """ Write the results of the query to the CSV """
43 create_dir(results_path)
44 filec = os.path.join(results_path, csv_results_file_prefix + ".csv")
45 with open(filec, "wb") as f:
46 f.write(query_results["Columns"].replace(" ", ",") + "\n")
47 for row in (query_results["Output"].replace("} {", ",").
48 replace("{", "").replace("}", "").split(",")):
49 f.write(row.replace(" ", ",") + "\n")
52 def positive_int(value):
53 """ Positive Integer type for Arguments """
56 raise argparse.ArgumentTypeError(
57 "%s is an invalid positive int value" % value)
61 def percent_float(value):
62 """ Floating type for Arguments """
64 if pvalue < 0.0 or pvalue > 100.0:
65 raise argparse.ArgumentTypeError(
66 "%s not in range [0.0, 100.0]" % pvalue)
71 """ Read the arguments, Invoke Test and Return the results"""
72 parser = argparse.ArgumentParser()
74 required_named = parser.add_argument_group("required named arguments")
75 required_named.add_argument("--lab_server_addr",
77 help=("The IP address of the"
78 "Spirent Lab Server"),
79 dest="lab_server_addr")
80 required_named.add_argument("--license_server_addr",
82 help=("The IP address of the Spirent"
84 dest="license_server_addr")
85 required_named.add_argument("--east_chassis_addr",
87 help=("The TestCenter chassis IP address to"
88 "use for the east test port"),
89 dest="east_chassis_addr")
90 required_named.add_argument("--east_slot_num",
93 help=("The TestCenter slot number to"
94 "use for the east test port"),
96 required_named.add_argument("--east_port_num",
99 help=("The TestCenter port number to use"
100 "for the east test port"),
101 dest="east_port_num")
102 required_named.add_argument("--west_chassis_addr",
104 help=("The TestCenter chassis IP address"
105 "to use for the west test port"),
106 dest="west_chassis_addr")
107 required_named.add_argument("--west_slot_num",
110 help=("The TestCenter slot number to use"
111 "for the west test port"),
112 dest="west_slot_num")
113 required_named.add_argument("--west_port_num",
116 help=("The TestCenter port number to"
117 "use for the west test port"),
118 dest="west_port_num")
119 # Optional parameters
120 optional_named = parser.add_argument_group("optional named arguments")
121 optional_named.add_argument("--metric",
123 help=("One among - throughput, latency,\
124 backtoback and frameloss"),
125 choices=["throughput", "latency",
126 "backtoback", "frameloss"],
127 default="throughput",
129 optional_named.add_argument("--test_session_name",
131 default="RFC2544 East-West Throughput",
132 help=("The friendly name to identify"
133 "the Spirent Lab Server test session"),
134 dest="test_session_name")
136 optional_named.add_argument("--test_user_name",
138 default="RFC2544 East-West User",
139 help=("The friendly name to identify the"
140 "Spirent Lab Server test user"),
141 dest="test_user_name")
142 optional_named.add_argument("--results_dir",
145 help="The directory to copy results to",
147 optional_named.add_argument("--csv_results_file_prefix",
149 default="Rfc2544Tput",
150 help="The prefix for the CSV results files",
151 dest="csv_results_file_prefix")
152 optional_named.add_argument("--num_trials",
156 help=("The number of trials to execute during"
159 optional_named.add_argument("--trial_duration_sec",
163 help=("The duration of each trial executed"
165 dest="trial_duration_sec")
166 optional_named.add_argument("--traffic_pattern",
168 choices=["BACKBONE", "MESH", "PAIR"],
170 help="The traffic pattern between endpoints",
171 dest="traffic_pattern")
172 optional_named.add_argument("--traffic_custom",
175 help="The traffic pattern between endpoints",
176 dest="traffic_custom")
177 optional_named.add_argument("--search_mode",
179 choices=["COMBO", "STEP", "BINARY"],
181 help=("The search mode used to find the"
184 optional_named.add_argument("--learning_mode",
186 choices=["AUTO", "L2_LEARNING",
187 "L3_LEARNING", "NONE"],
189 help=("The learning mode used during the test,"
190 "default is 'NONE'"),
191 dest="learning_mode")
192 optional_named.add_argument("--rate_lower_limit_pct",
196 help=("The minimum percent line rate that"
197 "will be used during the test"),
198 dest="rate_lower_limit_pct")
199 optional_named.add_argument("--rate_upper_limit_pct",
203 help=("The maximum percent line rate that"
204 "will be used during the test"),
205 dest="rate_upper_limit_pct")
206 optional_named.add_argument("--rate_initial_pct",
210 help=("If Search Mode is BINARY, the percent"
211 "line rate that will be used at the"
212 "start of the test"),
213 dest="rate_initial_pct")
214 optional_named.add_argument("--rate_step_pct",
218 help=("If SearchMode is STEP, the percent"
219 "load increase per step"),
220 dest="rate_step_pct")
221 optional_named.add_argument("--resolution_pct",
225 help=("The minimum percentage of load"
226 "adjustment between iterations"),
227 dest="resolution_pct")
228 optional_named.add_argument("--frame_size_list",
229 type=lambda s: [int(item)
230 for item in s.split(',')],
233 help="A comma-delimited list of frame sizes",
234 dest="frame_size_list")
235 optional_named.add_argument("--acceptable_frame_loss_pct",
239 help=("The maximum acceptable frame loss"
240 "percent in any iteration"),
241 dest="acceptable_frame_loss_pct")
242 optional_named.add_argument("--east_intf_addr",
244 default="192.85.1.3",
245 help=("The address to assign to the first"
246 "emulated device interface on the first"
248 dest="east_intf_addr")
249 optional_named.add_argument("--east_intf_gateway_addr",
251 default="192.85.1.53",
252 help=("The gateway address to assign to the"
253 "first emulated device interface on the"
255 dest="east_intf_gateway_addr")
256 optional_named.add_argument("--west_intf_addr",
258 default="192.85.1.53",
259 help=("The address to assign to the first"
260 "emulated device interface on the"
262 dest="west_intf_addr")
263 optional_named.add_argument("--west_intf_gateway_addr",
265 default="192.85.1.53",
266 help=("The gateway address to assign to"
267 "the first emulated device interface"
268 "on the first west port"),
269 dest="west_intf_gateway_addr")
270 parser.add_argument("-v",
274 help="More output during operation when present",
277 args = parser.parse_args()
280 logger.debug("Creating results directory")
281 create_dir(args.results_dir)
283 session_name = args.test_session_name
284 user_name = args.test_user_name
287 # Load Spirent REST Library
288 from stcrestclient import stchttp
290 stc = stchttp.StcHttp(args.lab_server_addr)
291 session_id = stc.new_session(user_name, session_name)
292 stc.join_session(session_id)
293 except RuntimeError as e:
297 # Get STC system info.
298 tx_port_loc = "//%s/%s/%s" % (args.east_chassis_addr,
301 rx_port_loc = "//%s/%s/%s" % (args.west_chassis_addr,
305 # Retrieve and display the server information
307 logger.debug("SpirentTestCenter system version: %s",
308 stc.get("system1", "version"))
314 logger.debug("Bring up license server")
315 license_mgr = stc.get("system1", "children-licenseservermanager")
317 logger.debug("license_mgr = %s", license_mgr)
318 stc.create("LicenseServer", under=license_mgr, attributes={
319 "server": args.license_server_addr})
321 # Create the root project object
323 logger.debug("Creating project ...")
324 project = stc.get("System1", "children-Project")
326 # Configure any custom traffic parameters
327 if args.traffic_custom == "cont":
329 logger.debug("Configure Continuous Traffic")
330 stc.create("ContinuousTestConfig", under=project)
334 logger.debug("Creating ports ...")
335 east_chassis_port = stc.create('port', project)
337 logger.debug("Configuring TX port ...")
338 stc.config(east_chassis_port, {'location': tx_port_loc})
339 port_list.append(east_chassis_port)
341 west_chassis_port = stc.create('port', project)
343 logger.debug("Configuring RX port ...")
344 stc.config(west_chassis_port, {'location': rx_port_loc})
345 port_list.append(west_chassis_port)
347 # Create emulated genparam for east port
348 east_device_gen_params = stc.create("EmulatedDeviceGenParams",
352 # Create the DeviceGenEthIIIfParams object
353 stc.create("DeviceGenEthIIIfParams",
354 under=east_device_gen_params)
355 # Configuring Ipv4 interfaces
356 stc.create("DeviceGenIpv4IfParams",
357 under=east_device_gen_params,
358 attributes={"Addr": args.east_intf_addr,
359 "Gateway": args.east_intf_gateway_addr})
360 # Create Devices using the Device Wizard
361 device_gen_config = stc.perform("DeviceGenConfigExpand",
362 params={"DeleteExisting": "No",
364 east_device_gen_params})
365 # Append to the device list
366 device_list.append(device_gen_config['ReturnList'])
368 # Create emulated genparam for west port
369 west_device_gen_params = stc.create("EmulatedDeviceGenParams",
373 # Create the DeviceGenEthIIIfParams object
374 stc.create("DeviceGenEthIIIfParams",
375 under=west_device_gen_params)
376 # Configuring Ipv4 interfaces
377 stc.create("DeviceGenIpv4IfParams",
378 under=west_device_gen_params,
379 attributes={"Addr": args.west_intf_addr,
380 "Gateway": args.west_intf_gateway_addr})
381 # Create Devices using the Device Wizard
382 device_gen_config = stc.perform("DeviceGenConfigExpand",
383 params={"DeleteExisting": "No",
385 west_device_gen_params})
386 # Append to the device list
387 device_list.append(device_gen_config['ReturnList'])
389 logger.debug(device_list)
391 # Create the RFC 2544 'metric test
392 if args.metric == "throughput":
394 logger.debug("Set up the RFC2544 throughput test...")
395 stc.perform("Rfc2544SetupThroughputTestCommand",
396 params={"AcceptableFrameLoss":
397 args.acceptable_frame_loss_pct,
398 "Duration": args.trial_duration_sec,
399 "FrameSizeList": args.frame_size_list,
400 "LearningMode": args.learning_mode,
401 "NumOfTrials": args.num_trials,
402 "RateInitial": args.rate_initial_pct,
403 "RateLowerLimit": args.rate_lower_limit_pct,
404 "RateStep": args.rate_step_pct,
405 "RateUpperLimit": args.rate_upper_limit_pct,
406 "Resolution": args.resolution_pct,
407 "SearchMode": args.search_mode,
408 "TrafficPattern": args.traffic_pattern})
409 elif args.metric == "backtoback":
410 stc.perform("Rfc2544SetupBackToBackTestCommand",
411 params={"AcceptableFrameLoss":
412 args.acceptable_frame_loss_pct,
413 "Duration": args.trial_duration_sec,
414 "FrameSizeList": args.frame_size_list,
415 "LearningMode": args.learning_mode,
416 "LatencyType": args.latency_type,
417 "NumOfTrials": args.num_trials,
418 "RateInitial": args.rate_initial_pct,
419 "RateLowerLimit": args.rate_lower_limit_pct,
420 "RateStep": args.rate_step_pct,
421 "RateUpperLimit": args.rate_upper_limit_pct,
422 "Resolution": args.resolution_pct,
423 "SearchMode": args.search_mode,
424 "TrafficPattern": args.traffic_pattern})
425 elif args.metric == "frameloss":
426 stc.perform("Rfc2544SetupFrameLossTestCommand",
427 params={"AcceptableFrameLoss":
428 args.acceptable_frame_loss_pct,
429 "Duration": args.trial_duration_sec,
430 "FrameSizeList": args.frame_size_list,
431 "LearningMode": args.learning_mode,
432 "LatencyType": args.latency_type,
433 "NumOfTrials": args.num_trials,
434 "RateInitial": args.rate_initial_pct,
435 "RateLowerLimit": args.rate_lower_limit_pct,
436 "RateStep": args.rate_step_pct,
437 "RateUpperLimit": args.rate_upper_limit_pct,
438 "Resolution": args.resolution_pct,
439 "SearchMode": args.search_mode,
440 "TrafficPattern": args.traffic_pattern})
441 elif args.metric == "latency":
442 stc.perform("Rfc2544SetupLatencyTestCommand",
443 params={"AcceptableFrameLoss":
444 args.acceptable_frame_loss_pct,
445 "Duration": args.trial_duration_sec,
446 "FrameSizeList": args.frame_size_list,
447 "LearningMode": args.learning_mode,
448 "LatencyType": args.latency_type,
449 "NumOfTrials": args.num_trials,
450 "RateInitial": args.rate_initial_pct,
451 "RateLowerLimit": args.rate_lower_limit_pct,
452 "RateStep": args.rate_step_pct,
453 "RateUpperLimit": args.rate_upper_limit_pct,
454 "Resolution": args.resolution_pct,
455 "SearchMode": args.search_mode,
456 "TrafficPattern": args.traffic_pattern})
458 # Save the configuration
459 stc.perform("SaveToTcc", params={"Filename": "2544.tcc"})
460 # Connect to the hardware...
461 stc.perform("AttachPorts", params={"portList": stc.get(
462 "system1.project", "children-port"), "autoConnect": "TRUE"})
463 # Apply configuration.
465 logger.debug("Apply configuration...")
469 logger.debug("Starting the sequencer...")
470 stc.perform("SequencerStart")
472 # Wait for sequencer to finish
474 "Starting test... Please wait for the test to complete...")
475 stc.wait_until_complete()
476 logger.info("The test has completed... Saving results...")
478 # Determine what the results database filename is...
479 lab_server_resultsdb = stc.get(
480 "system1.project.TestResultSetting", "CurrentResultFileName")
483 logger.debug("The lab server results database is %s",
484 lab_server_resultsdb)
486 stc.perform("CSSynchronizeFiles",
487 params={"DefaultDownloadDir": args.results_dir})
489 resultsdb = args.results_dir + \
490 lab_server_resultsdb.split("/Results")[1]
493 "The local summary DB file has been saved to %s", resultsdb)
495 # The returns the "RFC2544ThroughputTestResultDetailedSummaryView"
496 # table view from the results database.
497 # There are other views available.
499 if args.metric == "throughput":
501 stc.perform("QueryResult",
503 "DatabaseConnectionString":
506 ("RFC2544ThroughputTestResultDetailed"
509 # The returns the "RFC2544BacktoBackTestResultDetailedSummaryView"
510 # table view from the results database.
511 # There are other views available.
512 elif args.metric == "backtoback":
514 stc.perform("QueryResult",
516 "DatabaseConnectionString":
519 ("RFC2544Back2BackTestResultDetailed"
522 # The returns the "RFC2544LatencyTestResultDetailedSummaryView"
523 # table view from the results database.
524 # There are other views available.
525 elif args.metric == "latency":
527 stc.perform("QueryResult",
529 "DatabaseConnectionString":
532 ("RFC2544LatencyTestResultDetailed"
535 # The returns the "RFC2544FrameLossTestResultDetailedSummaryView"
536 # table view from the results database.
537 # There are other views available.
538 elif args.metric == "frameloss":
540 stc.perform("QueryResult",
542 "DatabaseConnectionString":
545 ("RFC2544FrameLossTestResultDetailed"
548 logger.debug("resultsdict[\"Columns\"]: %s",
549 resultsdict["Columns"])
550 logger.debug("resultsdict[\"Output\"]: %s", resultsdict["Output"])
551 logger.debug("Result paths: %s",
552 stc.perform("GetTestResultSettingPaths"))
554 # Write results to csv
555 logger.debug("Writing CSV file to results directory %s",
557 write_query_results_to_csv(
558 args.results_dir, args.csv_results_file_prefix, resultsdict)
560 except RuntimeError as e:
564 logger.debug("Destroy session on lab server")
567 logger.info("Test complete!")
569 if __name__ == "__main__":