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
25 from conf import settings
28 logger = logging.getLogger(__name__)
32 """Create the directory as specified in path """
33 if not os.path.exists(path):
37 logger.error("Failed to create directory %s: %s", path, str(e))
41 def write_query_results_to_csv(results_path, csv_results_file_prefix,
43 """ Write the results of the query to the CSV """
44 create_dir(results_path)
45 filec = os.path.join(results_path, csv_results_file_prefix + ".csv")
46 with open(filec, "wb") as f:
47 f.write(query_results["Columns"].replace(" ", ",") + "\n")
48 for row in (query_results["Output"].replace("} {", ",").
49 replace("{", "").replace("}", "").split(",")):
50 f.write(row.replace(" ", ",") + "\n")
53 def positive_int(value):
54 """ Positive Integer type for Arguments """
57 raise argparse.ArgumentTypeError(
58 "%s is an invalid positive int value" % value)
62 def percent_float(value):
63 """ Floating type for Arguments """
65 if pvalue < 0.0 or pvalue > 100.0:
66 raise argparse.ArgumentTypeError(
67 "%s not in range [0.0, 100.0]" % pvalue)
72 """ Read the arguments, Invoke Test and Return the results"""
73 parser = argparse.ArgumentParser()
75 required_named = parser.add_argument_group("required named arguments")
76 required_named.add_argument("--lab_server_addr",
78 help=("The IP address of the"
79 "Spirent Lab Server"),
80 dest="lab_server_addr")
81 required_named.add_argument("--license_server_addr",
83 help=("The IP address of the Spirent"
85 dest="license_server_addr")
86 required_named.add_argument("--east_chassis_addr",
88 help=("The TestCenter chassis IP address to"
89 "use for the east test port"),
90 dest="east_chassis_addr")
91 required_named.add_argument("--east_slot_num",
94 help=("The TestCenter slot number to"
95 "use for the east test port"),
97 required_named.add_argument("--east_port_num",
100 help=("The TestCenter port number to use"
101 "for the east test port"),
102 dest="east_port_num")
103 required_named.add_argument("--west_chassis_addr",
105 help=("The TestCenter chassis IP address"
106 "to use for the west test port"),
107 dest="west_chassis_addr")
108 required_named.add_argument("--west_slot_num",
111 help=("The TestCenter slot number to use"
112 "for the west test port"),
113 dest="west_slot_num")
114 required_named.add_argument("--west_port_num",
117 help=("The TestCenter port number to"
118 "use for the west test port"),
119 dest="west_port_num")
120 # Optional parameters
121 optional_named = parser.add_argument_group("optional named arguments")
122 optional_named.add_argument("--metric",
124 help=("One among - throughput, latency,\
125 backtoback and frameloss"),
126 choices=["throughput", "latency",
127 "backtoback", "frameloss"],
128 default="throughput",
130 optional_named.add_argument("--test_session_name",
132 default="RFC2544 East-West Throughput",
133 help=("The friendly name to identify"
134 "the Spirent Lab Server test session"),
135 dest="test_session_name")
137 optional_named.add_argument("--test_user_name",
139 default="RFC2544 East-West User",
140 help=("The friendly name to identify the"
141 "Spirent Lab Server test user"),
142 dest="test_user_name")
143 optional_named.add_argument("--results_dir",
145 default=settings.getValue("TRAFFICGEN_STC_RESULTS_DIR"),
146 help="The directory to copy results to",
148 optional_named.add_argument("--csv_results_file_prefix",
150 default="Rfc2544Tput",
151 help="The prefix for the CSV results files",
152 dest="csv_results_file_prefix")
153 optional_named.add_argument("--num_trials",
157 help=("The number of trials to execute during"
160 optional_named.add_argument("--trial_duration_sec",
164 help=("The duration of each trial executed"
166 dest="trial_duration_sec")
167 optional_named.add_argument("--traffic_pattern",
169 choices=["BACKBONE", "MESH", "PAIR"],
171 help="The traffic pattern between endpoints",
172 dest="traffic_pattern")
173 optional_named.add_argument("--traffic_custom",
176 help="The traffic pattern between endpoints",
177 dest="traffic_custom")
178 optional_named.add_argument("--search_mode",
180 choices=["COMBO", "STEP", "BINARY"],
182 help=("The search mode used to find the"
185 optional_named.add_argument("--learning_mode",
187 choices=["AUTO", "L2_LEARNING",
188 "L3_LEARNING", "NONE"],
190 help=("The learning mode used during the test,"
191 "default is 'NONE'"),
192 dest="learning_mode")
193 optional_named.add_argument("--rate_lower_limit_pct",
197 help=("The minimum percent line rate that"
198 "will be used during the test"),
199 dest="rate_lower_limit_pct")
200 optional_named.add_argument("--rate_upper_limit_pct",
204 help=("The maximum percent line rate that"
205 "will be used during the test"),
206 dest="rate_upper_limit_pct")
207 optional_named.add_argument("--rate_initial_pct",
211 help=("If Search Mode is BINARY, the percent"
212 "line rate that will be used at the"
213 "start of the test"),
214 dest="rate_initial_pct")
215 optional_named.add_argument("--rate_step_pct",
219 help=("If SearchMode is STEP, the percent"
220 "load increase per step"),
221 dest="rate_step_pct")
222 optional_named.add_argument("--resolution_pct",
226 help=("The minimum percentage of load"
227 "adjustment between iterations"),
228 dest="resolution_pct")
229 optional_named.add_argument("--frame_size_list",
230 type=lambda s: [int(item)
231 for item in s.split(',')],
234 help="A comma-delimited list of frame sizes",
235 dest="frame_size_list")
236 optional_named.add_argument("--acceptable_frame_loss_pct",
240 help=("The maximum acceptable frame loss"
241 "percent in any iteration"),
242 dest="acceptable_frame_loss_pct")
243 optional_named.add_argument("--east_intf_addr",
245 default="192.85.1.3",
246 help=("The address to assign to the first"
247 "emulated device interface on the first"
249 dest="east_intf_addr")
250 optional_named.add_argument("--east_intf_gateway_addr",
252 default="192.85.1.53",
253 help=("The gateway address to assign to the"
254 "first emulated device interface on the"
256 dest="east_intf_gateway_addr")
257 optional_named.add_argument("--west_intf_addr",
259 default="192.85.1.53",
260 help=("The address to assign to the first"
261 "emulated device interface on the"
263 dest="west_intf_addr")
264 optional_named.add_argument("--west_intf_gateway_addr",
266 default="192.85.1.53",
267 help=("The gateway address to assign to"
268 "the first emulated device interface"
269 "on the first west port"),
270 dest="west_intf_gateway_addr")
271 parser.add_argument("-v",
275 help="More output during operation when present",
278 args = parser.parse_args()
281 logger.debug("Creating results directory")
282 create_dir(args.results_dir)
284 session_name = args.test_session_name
285 user_name = args.test_user_name
288 # Load Spirent REST Library
289 from stcrestclient import stchttp
291 stc = stchttp.StcHttp(args.lab_server_addr)
292 session_id = stc.new_session(user_name, session_name)
293 stc.join_session(session_id)
294 except RuntimeError as e:
298 # Get STC system info.
299 tx_port_loc = "//%s/%s/%s" % (args.east_chassis_addr,
302 rx_port_loc = "//%s/%s/%s" % (args.west_chassis_addr,
306 # Retrieve and display the server information
308 logger.debug("SpirentTestCenter system version: %s",
309 stc.get("system1", "version"))
315 logger.debug("Bring up license server")
316 license_mgr = stc.get("system1", "children-licenseservermanager")
318 logger.debug("license_mgr = %s", license_mgr)
319 stc.create("LicenseServer", under=license_mgr, attributes={
320 "server": args.license_server_addr})
322 # Create the root project object
324 logger.debug("Creating project ...")
325 project = stc.get("System1", "children-Project")
327 # Configure any custom traffic parameters
328 if args.traffic_custom == "cont":
330 logger.debug("Configure Continuous Traffic")
331 stc.create("ContinuousTestConfig", under=project)
335 logger.debug("Creating ports ...")
336 east_chassis_port = stc.create('port', project)
338 logger.debug("Configuring TX port ...")
339 stc.config(east_chassis_port, {'location': tx_port_loc})
340 port_list.append(east_chassis_port)
342 west_chassis_port = stc.create('port', project)
344 logger.debug("Configuring RX port ...")
345 stc.config(west_chassis_port, {'location': rx_port_loc})
346 port_list.append(west_chassis_port)
348 # Create emulated genparam for east port
349 east_device_gen_params = stc.create("EmulatedDeviceGenParams",
353 # Create the DeviceGenEthIIIfParams object
354 stc.create("DeviceGenEthIIIfParams",
355 under=east_device_gen_params)
356 # Configuring Ipv4 interfaces
357 stc.create("DeviceGenIpv4IfParams",
358 under=east_device_gen_params,
359 attributes={"Addr": args.east_intf_addr,
360 "Gateway": args.east_intf_gateway_addr})
361 # Create Devices using the Device Wizard
362 device_gen_config = stc.perform("DeviceGenConfigExpand",
363 params={"DeleteExisting": "No",
365 east_device_gen_params})
366 # Append to the device list
367 device_list.append(device_gen_config['ReturnList'])
369 # Create emulated genparam for west port
370 west_device_gen_params = stc.create("EmulatedDeviceGenParams",
374 # Create the DeviceGenEthIIIfParams object
375 stc.create("DeviceGenEthIIIfParams",
376 under=west_device_gen_params)
377 # Configuring Ipv4 interfaces
378 stc.create("DeviceGenIpv4IfParams",
379 under=west_device_gen_params,
380 attributes={"Addr": args.west_intf_addr,
381 "Gateway": args.west_intf_gateway_addr})
382 # Create Devices using the Device Wizard
383 device_gen_config = stc.perform("DeviceGenConfigExpand",
384 params={"DeleteExisting": "No",
386 west_device_gen_params})
387 # Append to the device list
388 device_list.append(device_gen_config['ReturnList'])
390 logger.debug(device_list)
392 # Create the RFC 2544 'metric test
393 if args.metric == "throughput":
395 logger.debug("Set up the RFC2544 throughput test...")
396 stc.perform("Rfc2544SetupThroughputTestCommand",
397 params={"AcceptableFrameLoss":
398 args.acceptable_frame_loss_pct,
399 "Duration": args.trial_duration_sec,
400 "FrameSizeList": args.frame_size_list,
401 "LearningMode": args.learning_mode,
402 "NumOfTrials": args.num_trials,
403 "RateInitial": args.rate_initial_pct,
404 "RateLowerLimit": args.rate_lower_limit_pct,
405 "RateStep": args.rate_step_pct,
406 "RateUpperLimit": args.rate_upper_limit_pct,
407 "Resolution": args.resolution_pct,
408 "SearchMode": args.search_mode,
409 "TrafficPattern": args.traffic_pattern})
410 elif args.metric == "backtoback":
411 stc.perform("Rfc2544SetupBackToBackTestCommand",
412 params={"AcceptableFrameLoss":
413 args.acceptable_frame_loss_pct,
414 "Duration": args.trial_duration_sec,
415 "FrameSizeList": args.frame_size_list,
416 "LearningMode": args.learning_mode,
417 "LatencyType": args.latency_type,
418 "NumOfTrials": args.num_trials,
419 "RateInitial": args.rate_initial_pct,
420 "RateLowerLimit": args.rate_lower_limit_pct,
421 "RateStep": args.rate_step_pct,
422 "RateUpperLimit": args.rate_upper_limit_pct,
423 "Resolution": args.resolution_pct,
424 "SearchMode": args.search_mode,
425 "TrafficPattern": args.traffic_pattern})
426 elif args.metric == "frameloss":
427 stc.perform("Rfc2544SetupFrameLossTestCommand",
428 params={"AcceptableFrameLoss":
429 args.acceptable_frame_loss_pct,
430 "Duration": args.trial_duration_sec,
431 "FrameSizeList": args.frame_size_list,
432 "LearningMode": args.learning_mode,
433 "LatencyType": args.latency_type,
434 "NumOfTrials": args.num_trials,
435 "RateInitial": args.rate_initial_pct,
436 "RateLowerLimit": args.rate_lower_limit_pct,
437 "RateStep": args.rate_step_pct,
438 "RateUpperLimit": args.rate_upper_limit_pct,
439 "Resolution": args.resolution_pct,
440 "SearchMode": args.search_mode,
441 "TrafficPattern": args.traffic_pattern})
442 elif args.metric == "latency":
443 stc.perform("Rfc2544SetupLatencyTestCommand",
444 params={"AcceptableFrameLoss":
445 args.acceptable_frame_loss_pct,
446 "Duration": args.trial_duration_sec,
447 "FrameSizeList": args.frame_size_list,
448 "LearningMode": args.learning_mode,
449 "LatencyType": args.latency_type,
450 "NumOfTrials": args.num_trials,
451 "RateInitial": args.rate_initial_pct,
452 "RateLowerLimit": args.rate_lower_limit_pct,
453 "RateStep": args.rate_step_pct,
454 "RateUpperLimit": args.rate_upper_limit_pct,
455 "Resolution": args.resolution_pct,
456 "SearchMode": args.search_mode,
457 "TrafficPattern": args.traffic_pattern})
459 # Save the configuration
460 stc.perform("SaveToTcc", params={"Filename": "2544.tcc"})
461 # Connect to the hardware...
462 stc.perform("AttachPorts", params={"portList": stc.get(
463 "system1.project", "children-port"), "autoConnect": "TRUE"})
464 # Apply configuration.
466 logger.debug("Apply configuration...")
470 logger.debug("Starting the sequencer...")
471 stc.perform("SequencerStart")
473 # Wait for sequencer to finish
475 "Starting test... Please wait for the test to complete...")
476 stc.wait_until_complete()
477 logger.info("The test has completed... Saving results...")
479 # Determine what the results database filename is...
480 lab_server_resultsdb = stc.get(
481 "system1.project.TestResultSetting", "CurrentResultFileName")
484 logger.debug("The lab server results database is %s",
485 lab_server_resultsdb)
487 stc.perform("CSSynchronizeFiles",
488 params={"DefaultDownloadDir": args.results_dir})
490 resultsdb = args.results_dir + \
491 lab_server_resultsdb.split("/Results")[1]
494 "The local summary DB file has been saved to %s", resultsdb)
496 # The returns the "RFC2544ThroughputTestResultDetailedSummaryView"
497 # table view from the results database.
498 # There are other views available.
500 if args.metric == "throughput":
502 stc.perform("QueryResult",
504 "DatabaseConnectionString":
507 ("RFC2544ThroughputTestResultDetailed"
510 # The returns the "RFC2544BacktoBackTestResultDetailedSummaryView"
511 # table view from the results database.
512 # There are other views available.
513 elif args.metric == "backtoback":
515 stc.perform("QueryResult",
517 "DatabaseConnectionString":
520 ("RFC2544Back2BackTestResultDetailed"
523 # The returns the "RFC2544LatencyTestResultDetailedSummaryView"
524 # table view from the results database.
525 # There are other views available.
526 elif args.metric == "latency":
528 stc.perform("QueryResult",
530 "DatabaseConnectionString":
533 ("RFC2544LatencyTestResultDetailed"
536 # The returns the "RFC2544FrameLossTestResultDetailedSummaryView"
537 # table view from the results database.
538 # There are other views available.
539 elif args.metric == "frameloss":
541 stc.perform("QueryResult",
543 "DatabaseConnectionString":
546 ("RFC2544FrameLossTestResultDetailed"
549 logger.debug("resultsdict[\"Columns\"]: %s",
550 resultsdict["Columns"])
551 logger.debug("resultsdict[\"Output\"]: %s", resultsdict["Output"])
552 logger.debug("Result paths: %s",
553 stc.perform("GetTestResultSettingPaths"))
555 # Write results to csv
556 logger.debug("Writing CSV file to results directory %s",
558 write_query_results_to_csv(
559 args.results_dir, args.csv_results_file_prefix, resultsdict)
561 except RuntimeError as e:
565 logger.debug("Destroy session on lab server")
568 logger.info("Test complete!")
570 if __name__ == "__main__":