1 # Copyright 2016-2017 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.
15 # Invalid name of file, must be used '_' instead '-'
16 # pylint: disable=invalid-name
18 @author Spirent Communications
20 This test automates the RFC2544 tests using the Spirent
21 TestCenter REST APIs. This test supports Python 3.4
29 _LOGGER = logging.getLogger(__name__)
33 """Create the directory as specified in path """
34 if not os.path.exists(path):
38 _LOGGER.error("Failed to create directory %s: %s", path, str(ex))
42 def write_query_results_to_csv(results_path, csv_results_file_prefix,
44 """ Write the results of the query to the CSV """
45 create_dir(results_path)
46 filec = os.path.join(results_path, csv_results_file_prefix + ".csv")
47 with open(filec, "wb") as result_file:
48 result_file.write(query_results["Columns"].replace(" ", ",") + "\n")
49 for row in (query_results["Output"].replace("} {", ",").
50 replace("{", "").replace("}", "").split(",")):
51 result_file.write(row.replace(" ", ",") + "\n")
54 def positive_int(value):
55 """ Positive Integer type for Arguments """
58 raise argparse.ArgumentTypeError(
59 "%s is an invalid positive int value" % value)
63 def percent_float(value):
64 """ Floating type for Arguments """
66 if pvalue < 0.0 or pvalue > 100.0:
67 raise argparse.ArgumentTypeError(
68 "%s not in range [0.0, 100.0]" % pvalue)
71 # pylint: disable=too-many-branches, too-many-statements
73 """ Read the arguments, Invoke Test and Return the results"""
74 parser = argparse.ArgumentParser()
76 required_named = parser.add_argument_group("required named arguments")
77 required_named.add_argument("--lab_server_addr",
79 help=("The IP address of the"
80 "Spirent Lab Server"),
81 dest="lab_server_addr")
82 required_named.add_argument("--license_server_addr",
84 help=("The IP address of the Spirent"
86 dest="license_server_addr")
87 required_named.add_argument("--east_chassis_addr",
89 help=("The TestCenter chassis IP address to"
90 "use for the east test port"),
91 dest="east_chassis_addr")
92 required_named.add_argument("--east_slot_num",
95 help=("The TestCenter slot number to"
96 "use for the east test port"),
98 required_named.add_argument("--east_port_num",
101 help=("The TestCenter port number to use"
102 "for the east test port"),
103 dest="east_port_num")
104 required_named.add_argument("--west_chassis_addr",
106 help=("The TestCenter chassis IP address"
107 "to use for the west test port"),
108 dest="west_chassis_addr")
109 required_named.add_argument("--west_slot_num",
112 help=("The TestCenter slot number to use"
113 "for the west test port"),
114 dest="west_slot_num")
115 required_named.add_argument("--west_port_num",
118 help=("The TestCenter port number to"
119 "use for the west test port"),
120 dest="west_port_num")
121 # Optional parameters
122 optional_named = parser.add_argument_group("optional named arguments")
123 optional_named.add_argument("--metric",
125 help=("One among - throughput, latency,\
126 backtoback and frameloss"),
127 choices=["throughput", "latency",
128 "backtoback", "frameloss"],
129 default="throughput",
131 optional_named.add_argument("--test_session_name",
133 default="RFC2544 East-West Throughput",
134 help=("The friendly name to identify"
135 "the Spirent Lab Server test session"),
136 dest="test_session_name")
138 optional_named.add_argument("--test_user_name",
140 default="RFC2544 East-West User",
141 help=("The friendly name to identify the"
142 "Spirent Lab Server test user"),
143 dest="test_user_name")
144 optional_named.add_argument("--results_dir",
147 help="The directory to copy results to",
149 optional_named.add_argument("--csv_results_file_prefix",
151 default="Rfc2544Tput",
152 help="The prefix for the CSV results files",
153 dest="csv_results_file_prefix")
154 optional_named.add_argument("--num_trials",
158 help=("The number of trials to execute during"
161 optional_named.add_argument("--trial_duration_sec",
165 help=("The duration of each trial executed"
167 dest="trial_duration_sec")
168 optional_named.add_argument("--traffic_pattern",
170 choices=["BACKBONE", "MESH", "PAIR"],
172 help="The traffic pattern between endpoints",
173 dest="traffic_pattern")
174 optional_named.add_argument("--traffic_custom",
177 help="The traffic pattern between endpoints",
178 dest="traffic_custom")
179 optional_named.add_argument("--search_mode",
181 choices=["COMBO", "STEP", "BINARY"],
183 help=("The search mode used to find the"
186 optional_named.add_argument("--learning_mode",
188 choices=["AUTO", "L2_LEARNING",
189 "L3_LEARNING", "NONE"],
191 help=("The learning mode used during the test,"
192 "default is 'NONE'"),
193 dest="learning_mode")
194 optional_named.add_argument("--rate_lower_limit_pct",
198 help=("The minimum percent line rate that"
199 "will be used during the test"),
200 dest="rate_lower_limit_pct")
201 optional_named.add_argument("--rate_upper_limit_pct",
205 help=("The maximum percent line rate that"
206 "will be used during the test"),
207 dest="rate_upper_limit_pct")
208 optional_named.add_argument("--rate_initial_pct",
212 help=("If Search Mode is BINARY, the percent"
213 "line rate that will be used at the"
214 "start of the test"),
215 dest="rate_initial_pct")
216 optional_named.add_argument("--rate_step_pct",
220 help=("If SearchMode is STEP, the percent"
221 "load increase per step"),
222 dest="rate_step_pct")
223 optional_named.add_argument("--resolution_pct",
227 help=("The minimum percentage of load"
228 "adjustment between iterations"),
229 dest="resolution_pct")
230 optional_named.add_argument("--frame_size_list",
231 type=lambda s: [int(item)
232 for item in s.split(',')],
235 help="A comma-delimited list of frame sizes",
236 dest="frame_size_list")
237 optional_named.add_argument("--acceptable_frame_loss_pct",
241 help=("The maximum acceptable frame loss"
242 "percent in any iteration"),
243 dest="acceptable_frame_loss_pct")
244 optional_named.add_argument("--east_intf_addr",
246 default="192.85.1.3",
247 help=("The address to assign to the first"
248 "emulated device interface on the first"
250 dest="east_intf_addr")
251 optional_named.add_argument("--east_intf_gateway_addr",
253 default="192.85.1.53",
254 help=("The gateway address to assign to the"
255 "first emulated device interface on the"
257 dest="east_intf_gateway_addr")
258 optional_named.add_argument("--west_intf_addr",
260 default="192.85.1.53",
261 help=("The address to assign to the first"
262 "emulated device interface on the"
264 dest="west_intf_addr")
265 optional_named.add_argument("--west_intf_gateway_addr",
267 default="192.85.1.53",
268 help=("The gateway address to assign to"
269 "the first emulated device interface"
270 "on the first west port"),
271 dest="west_intf_gateway_addr")
272 parser.add_argument("-v",
276 help="More output during operation when present",
279 args = parser.parse_args()
282 _LOGGER.debug("Creating results directory")
283 create_dir(args.results_dir)
285 session_name = args.test_session_name
286 user_name = args.test_user_name
287 # pylint: disable=import-error
289 # Load Spirent REST Library
290 from stcrestclient import stchttp
292 stc = stchttp.StcHttp(args.lab_server_addr)
293 session_id = stc.new_session(user_name, session_name)
294 stc.join_session(session_id)
295 except RuntimeError as err:
299 # Get STC system info.
300 tx_port_loc = "//%s/%s/%s" % (args.east_chassis_addr,
303 rx_port_loc = "//%s/%s/%s" % (args.west_chassis_addr,
307 # Retrieve and display the server information
309 _LOGGER.debug("SpirentTestCenter system version: %s",
310 stc.get("system1", "version"))
316 _LOGGER.debug("Bring up license server")
317 license_mgr = stc.get("system1", "children-licenseservermanager")
319 _LOGGER.debug("license_mgr = %s", license_mgr)
320 stc.create("LicenseServer", under=license_mgr, attributes={
321 "server": args.license_server_addr})
323 # Create the root project object
325 _LOGGER.debug("Creating project ...")
326 project = stc.get("System1", "children-Project")
328 # Configure any custom traffic parameters
329 if args.traffic_custom == "cont":
331 _LOGGER.debug("Configure Continuous Traffic")
332 stc.create("ContinuousTestConfig", under=project)
336 _LOGGER.debug("Creating ports ...")
337 east_chassis_port = stc.create('port', project)
339 _LOGGER.debug("Configuring TX port ...")
340 stc.config(east_chassis_port, {'location': tx_port_loc})
341 port_list.append(east_chassis_port)
343 west_chassis_port = stc.create('port', project)
345 _LOGGER.debug("Configuring RX port ...")
346 stc.config(west_chassis_port, {'location': rx_port_loc})
347 port_list.append(west_chassis_port)
349 # Create emulated genparam for east port
350 east_device_gen_params = stc.create("EmulatedDeviceGenParams",
354 # Create the DeviceGenEthIIIfParams object
355 stc.create("DeviceGenEthIIIfParams",
356 under=east_device_gen_params)
357 # Configuring Ipv4 interfaces
358 stc.create("DeviceGenIpv4IfParams",
359 under=east_device_gen_params,
360 attributes={"Addr": args.east_intf_addr,
361 "Gateway": args.east_intf_gateway_addr})
362 # Create Devices using the Device Wizard
363 device_gen_config = stc.perform("DeviceGenConfigExpand",
364 params={"DeleteExisting": "No",
366 east_device_gen_params})
367 # Append to the device list
368 device_list.append(device_gen_config['ReturnList'])
370 # Create emulated genparam for west port
371 west_device_gen_params = stc.create("EmulatedDeviceGenParams",
375 # Create the DeviceGenEthIIIfParams object
376 stc.create("DeviceGenEthIIIfParams",
377 under=west_device_gen_params)
378 # Configuring Ipv4 interfaces
379 stc.create("DeviceGenIpv4IfParams",
380 under=west_device_gen_params,
381 attributes={"Addr": args.west_intf_addr,
382 "Gateway": args.west_intf_gateway_addr})
383 # Create Devices using the Device Wizard
384 device_gen_config = stc.perform("DeviceGenConfigExpand",
385 params={"DeleteExisting": "No",
387 west_device_gen_params})
388 # Append to the device list
389 device_list.append(device_gen_config['ReturnList'])
391 _LOGGER.debug(device_list)
393 # Create the RFC 2544 'metric test
394 if args.metric == "throughput":
396 _LOGGER.debug("Set up the RFC2544 throughput test...")
397 stc.perform("Rfc2544SetupThroughputTestCommand",
398 params={"AcceptableFrameLoss":
399 args.acceptable_frame_loss_pct,
400 "Duration": args.trial_duration_sec,
401 "FrameSizeList": args.frame_size_list,
402 "LearningMode": args.learning_mode,
403 "NumOfTrials": args.num_trials,
404 "RateInitial": args.rate_initial_pct,
405 "RateLowerLimit": args.rate_lower_limit_pct,
406 "RateStep": args.rate_step_pct,
407 "RateUpperLimit": args.rate_upper_limit_pct,
408 "Resolution": args.resolution_pct,
409 "SearchMode": args.search_mode,
410 "TrafficPattern": args.traffic_pattern})
411 elif args.metric == "backtoback":
412 stc.perform("Rfc2544SetupBackToBackTestCommand",
413 params={"AcceptableFrameLoss":
414 args.acceptable_frame_loss_pct,
415 "Duration": args.trial_duration_sec,
416 "FrameSizeList": args.frame_size_list,
417 "LearningMode": args.learning_mode,
418 "LatencyType": args.latency_type,
419 "NumOfTrials": args.num_trials,
420 "RateInitial": args.rate_initial_pct,
421 "RateLowerLimit": args.rate_lower_limit_pct,
422 "RateStep": args.rate_step_pct,
423 "RateUpperLimit": args.rate_upper_limit_pct,
424 "Resolution": args.resolution_pct,
425 "SearchMode": args.search_mode,
426 "TrafficPattern": args.traffic_pattern})
427 elif args.metric == "frameloss":
428 stc.perform("Rfc2544SetupFrameLossTestCommand",
429 params={"AcceptableFrameLoss":
430 args.acceptable_frame_loss_pct,
431 "Duration": args.trial_duration_sec,
432 "FrameSizeList": args.frame_size_list,
433 "LearningMode": args.learning_mode,
434 "LatencyType": args.latency_type,
435 "NumOfTrials": args.num_trials,
436 "RateInitial": args.rate_initial_pct,
437 "RateLowerLimit": args.rate_lower_limit_pct,
438 "RateStep": args.rate_step_pct,
439 "RateUpperLimit": args.rate_upper_limit_pct,
440 "Resolution": args.resolution_pct,
441 "SearchMode": args.search_mode,
442 "TrafficPattern": args.traffic_pattern})
443 elif args.metric == "latency":
444 stc.perform("Rfc2544SetupLatencyTestCommand",
445 params={"AcceptableFrameLoss":
446 args.acceptable_frame_loss_pct,
447 "Duration": args.trial_duration_sec,
448 "FrameSizeList": args.frame_size_list,
449 "LearningMode": args.learning_mode,
450 "LatencyType": args.latency_type,
451 "NumOfTrials": args.num_trials,
452 "RateInitial": args.rate_initial_pct,
453 "RateLowerLimit": args.rate_lower_limit_pct,
454 "RateStep": args.rate_step_pct,
455 "RateUpperLimit": args.rate_upper_limit_pct,
456 "Resolution": args.resolution_pct,
457 "SearchMode": args.search_mode,
458 "TrafficPattern": args.traffic_pattern})
460 # Save the configuration
461 stc.perform("SaveToTcc", params={"Filename": "2544.tcc"})
462 # Connect to the hardware...
463 stc.perform("AttachPorts", params={"portList": stc.get(
464 "system1.project", "children-port"), "autoConnect": "TRUE"})
465 # Apply configuration.
467 _LOGGER.debug("Apply configuration...")
471 _LOGGER.debug("Starting the sequencer...")
472 stc.perform("SequencerStart")
474 # Wait for sequencer to finish
476 "Starting test... Please wait for the test to complete...")
477 stc.wait_until_complete()
478 _LOGGER.info("The test has completed... Saving results...")
480 # Determine what the results database filename is...
481 lab_server_resultsdb = stc.get(
482 "system1.project.TestResultSetting", "CurrentResultFileName")
485 _LOGGER.debug("The lab server results database is %s",
486 lab_server_resultsdb)
488 stc.perform("CSSynchronizeFiles",
489 params={"DefaultDownloadDir": args.results_dir})
491 resultsdb = args.results_dir + \
492 lab_server_resultsdb.split("/Results")[1]
494 if not os.path.exists(resultsdb):
495 resultsdb = lab_server_resultsdb
496 _LOGGER.info("Failed to create the local summary DB File, using"
497 " the remote DB file instead.")
500 "The local summary DB file has been saved to %s", resultsdb)
502 # The returns the "RFC2544ThroughputTestResultDetailedSummaryView"
503 # table view from the results database.
504 # There are other views available.
506 if args.metric == "throughput":
508 stc.perform("QueryResult",
510 "DatabaseConnectionString":
513 ("RFC2544ThroughputTestResultDetailed"
516 # The returns the "RFC2544BacktoBackTestResultDetailedSummaryView"
517 # table view from the results database.
518 # There are other views available.
519 elif args.metric == "backtoback":
521 stc.perform("QueryResult",
523 "DatabaseConnectionString":
526 ("RFC2544Back2BackTestResultDetailed"
529 # The returns the "RFC2544LatencyTestResultDetailedSummaryView"
530 # table view from the results database.
531 # There are other views available.
532 elif args.metric == "latency":
534 stc.perform("QueryResult",
536 "DatabaseConnectionString":
539 ("RFC2544LatencyTestResultDetailed"
542 # The returns the "RFC2544FrameLossTestResultDetailedSummaryView"
543 # table view from the results database.
544 # There are other views available.
545 elif args.metric == "frameloss":
547 stc.perform("QueryResult",
549 "DatabaseConnectionString":
552 ("RFC2544FrameLossTestResultDetailed"
555 _LOGGER.debug("resultsdict[\"Columns\"]: %s",
556 resultsdict["Columns"])
557 _LOGGER.debug("resultsdict[\"Output\"]: %s", resultsdict["Output"])
558 _LOGGER.debug("Result paths: %s",
559 stc.perform("GetTestResultSettingPaths"))
561 # Write results to csv
562 _LOGGER.debug("Writing CSV file to results directory %s",
564 write_query_results_to_csv(
565 args.results_dir, args.csv_results_file_prefix, resultsdict)
567 except RuntimeError as e:
571 _LOGGER.debug("Destroy session on lab server")
574 _LOGGER.info("Test complete!")
576 if __name__ == "__main__":