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
27 from conf import settings
30 _LOGGER = logging.getLogger(__name__)
34 """Create the directory as specified in path """
35 if not os.path.exists(path):
39 _LOGGER.error("Failed to create directory %s: %s", path, str(ex))
43 def write_query_results_to_csv(results_path, csv_results_file_prefix,
45 """ Write the results of the query to the CSV """
46 create_dir(results_path)
47 filec = os.path.join(results_path, csv_results_file_prefix + ".csv")
48 with open(filec, "wb") as result_file:
49 result_file.write(query_results["Columns"].replace(" ", ",") + "\n")
50 for row in (query_results["Output"].replace("} {", ",").
51 replace("{", "").replace("}", "").split(",")):
52 result_file.write(row.replace(" ", ",") + "\n")
55 def positive_int(value):
56 """ Positive Integer type for Arguments """
59 raise argparse.ArgumentTypeError(
60 "%s is an invalid positive int value" % value)
64 def percent_float(value):
65 """ Floating type for Arguments """
67 if pvalue < 0.0 or pvalue > 100.0:
68 raise argparse.ArgumentTypeError(
69 "%s not in range [0.0, 100.0]" % pvalue)
72 # pylint: disable=too-many-branches, too-many-statements
74 """ Read the arguments, Invoke Test and Return the results"""
75 parser = argparse.ArgumentParser()
77 required_named = parser.add_argument_group("required named arguments")
78 required_named.add_argument("--lab_server_addr",
80 help=("The IP address of the"
81 "Spirent Lab Server"),
82 dest="lab_server_addr")
83 required_named.add_argument("--license_server_addr",
85 help=("The IP address of the Spirent"
87 dest="license_server_addr")
88 required_named.add_argument("--east_chassis_addr",
90 help=("The TestCenter chassis IP address to"
91 "use for the east test port"),
92 dest="east_chassis_addr")
93 required_named.add_argument("--east_slot_num",
96 help=("The TestCenter slot number to"
97 "use for the east test port"),
99 required_named.add_argument("--east_port_num",
102 help=("The TestCenter port number to use"
103 "for the east test port"),
104 dest="east_port_num")
105 required_named.add_argument("--west_chassis_addr",
107 help=("The TestCenter chassis IP address"
108 "to use for the west test port"),
109 dest="west_chassis_addr")
110 required_named.add_argument("--west_slot_num",
113 help=("The TestCenter slot number to use"
114 "for the west test port"),
115 dest="west_slot_num")
116 required_named.add_argument("--west_port_num",
119 help=("The TestCenter port number to"
120 "use for the west test port"),
121 dest="west_port_num")
122 # Optional parameters
123 optional_named = parser.add_argument_group("optional named arguments")
124 optional_named.add_argument("--metric",
126 help=("One among - throughput, latency,\
127 backtoback and frameloss"),
128 choices=["throughput", "latency",
129 "backtoback", "frameloss"],
130 default="throughput",
132 optional_named.add_argument("--test_session_name",
134 default="RFC2544 East-West Throughput",
135 help=("The friendly name to identify"
136 "the Spirent Lab Server test session"),
137 dest="test_session_name")
139 optional_named.add_argument("--test_user_name",
141 default="RFC2544 East-West User",
142 help=("The friendly name to identify the"
143 "Spirent Lab Server test user"),
144 dest="test_user_name")
145 optional_named.add_argument("--results_dir",
147 default=settings.getValue("TRAFFICGEN_STC_RESULTS_DIR"),
148 help="The directory to copy results to",
150 optional_named.add_argument("--csv_results_file_prefix",
152 default="Rfc2544Tput",
153 help="The prefix for the CSV results files",
154 dest="csv_results_file_prefix")
155 optional_named.add_argument("--num_trials",
159 help=("The number of trials to execute during"
162 optional_named.add_argument("--trial_duration_sec",
166 help=("The duration of each trial executed"
168 dest="trial_duration_sec")
169 optional_named.add_argument("--traffic_pattern",
171 choices=["BACKBONE", "MESH", "PAIR"],
173 help="The traffic pattern between endpoints",
174 dest="traffic_pattern")
175 optional_named.add_argument("--traffic_custom",
178 help="The traffic pattern between endpoints",
179 dest="traffic_custom")
180 optional_named.add_argument("--search_mode",
182 choices=["COMBO", "STEP", "BINARY"],
184 help=("The search mode used to find the"
187 optional_named.add_argument("--learning_mode",
189 choices=["AUTO", "L2_LEARNING",
190 "L3_LEARNING", "NONE"],
192 help=("The learning mode used during the test,"
193 "default is 'NONE'"),
194 dest="learning_mode")
195 optional_named.add_argument("--rate_lower_limit_pct",
199 help=("The minimum percent line rate that"
200 "will be used during the test"),
201 dest="rate_lower_limit_pct")
202 optional_named.add_argument("--rate_upper_limit_pct",
206 help=("The maximum percent line rate that"
207 "will be used during the test"),
208 dest="rate_upper_limit_pct")
209 optional_named.add_argument("--rate_initial_pct",
213 help=("If Search Mode is BINARY, the percent"
214 "line rate that will be used at the"
215 "start of the test"),
216 dest="rate_initial_pct")
217 optional_named.add_argument("--rate_step_pct",
221 help=("If SearchMode is STEP, the percent"
222 "load increase per step"),
223 dest="rate_step_pct")
224 optional_named.add_argument("--resolution_pct",
228 help=("The minimum percentage of load"
229 "adjustment between iterations"),
230 dest="resolution_pct")
231 optional_named.add_argument("--frame_size_list",
232 type=lambda s: [int(item)
233 for item in s.split(',')],
236 help="A comma-delimited list of frame sizes",
237 dest="frame_size_list")
238 optional_named.add_argument("--acceptable_frame_loss_pct",
242 help=("The maximum acceptable frame loss"
243 "percent in any iteration"),
244 dest="acceptable_frame_loss_pct")
245 optional_named.add_argument("--east_intf_addr",
247 default="192.85.1.3",
248 help=("The address to assign to the first"
249 "emulated device interface on the first"
251 dest="east_intf_addr")
252 optional_named.add_argument("--east_intf_gateway_addr",
254 default="192.85.1.53",
255 help=("The gateway address to assign to the"
256 "first emulated device interface on the"
258 dest="east_intf_gateway_addr")
259 optional_named.add_argument("--west_intf_addr",
261 default="192.85.1.53",
262 help=("The address to assign to the first"
263 "emulated device interface on the"
265 dest="west_intf_addr")
266 optional_named.add_argument("--west_intf_gateway_addr",
268 default="192.85.1.53",
269 help=("The gateway address to assign to"
270 "the first emulated device interface"
271 "on the first west port"),
272 dest="west_intf_gateway_addr")
273 parser.add_argument("-v",
277 help="More output during operation when present",
280 args = parser.parse_args()
283 _LOGGER.debug("Creating results directory")
284 create_dir(args.results_dir)
286 session_name = args.test_session_name
287 user_name = args.test_user_name
288 # pylint: disable=import-error
290 # Load Spirent REST Library
291 from stcrestclient import stchttp
293 stc = stchttp.StcHttp(args.lab_server_addr)
294 session_id = stc.new_session(user_name, session_name)
295 stc.join_session(session_id)
296 except RuntimeError as err:
300 # Get STC system info.
301 tx_port_loc = "//%s/%s/%s" % (args.east_chassis_addr,
304 rx_port_loc = "//%s/%s/%s" % (args.west_chassis_addr,
308 # Retrieve and display the server information
310 _LOGGER.debug("SpirentTestCenter system version: %s",
311 stc.get("system1", "version"))
317 _LOGGER.debug("Bring up license server")
318 license_mgr = stc.get("system1", "children-licenseservermanager")
320 _LOGGER.debug("license_mgr = %s", license_mgr)
321 stc.create("LicenseServer", under=license_mgr, attributes={
322 "server": args.license_server_addr})
324 # Create the root project object
326 _LOGGER.debug("Creating project ...")
327 project = stc.get("System1", "children-Project")
329 # Configure any custom traffic parameters
330 if args.traffic_custom == "cont":
332 _LOGGER.debug("Configure Continuous Traffic")
333 stc.create("ContinuousTestConfig", under=project)
337 _LOGGER.debug("Creating ports ...")
338 east_chassis_port = stc.create('port', project)
340 _LOGGER.debug("Configuring TX port ...")
341 stc.config(east_chassis_port, {'location': tx_port_loc})
342 port_list.append(east_chassis_port)
344 west_chassis_port = stc.create('port', project)
346 _LOGGER.debug("Configuring RX port ...")
347 stc.config(west_chassis_port, {'location': rx_port_loc})
348 port_list.append(west_chassis_port)
350 # Create emulated genparam for east port
351 east_device_gen_params = stc.create("EmulatedDeviceGenParams",
355 # Create the DeviceGenEthIIIfParams object
356 stc.create("DeviceGenEthIIIfParams",
357 under=east_device_gen_params)
358 # Configuring Ipv4 interfaces
359 stc.create("DeviceGenIpv4IfParams",
360 under=east_device_gen_params,
361 attributes={"Addr": args.east_intf_addr,
362 "Gateway": args.east_intf_gateway_addr})
363 # Create Devices using the Device Wizard
364 device_gen_config = stc.perform("DeviceGenConfigExpand",
365 params={"DeleteExisting": "No",
367 east_device_gen_params})
368 # Append to the device list
369 device_list.append(device_gen_config['ReturnList'])
371 # Create emulated genparam for west port
372 west_device_gen_params = stc.create("EmulatedDeviceGenParams",
376 # Create the DeviceGenEthIIIfParams object
377 stc.create("DeviceGenEthIIIfParams",
378 under=west_device_gen_params)
379 # Configuring Ipv4 interfaces
380 stc.create("DeviceGenIpv4IfParams",
381 under=west_device_gen_params,
382 attributes={"Addr": args.west_intf_addr,
383 "Gateway": args.west_intf_gateway_addr})
384 # Create Devices using the Device Wizard
385 device_gen_config = stc.perform("DeviceGenConfigExpand",
386 params={"DeleteExisting": "No",
388 west_device_gen_params})
389 # Append to the device list
390 device_list.append(device_gen_config['ReturnList'])
392 _LOGGER.debug(device_list)
394 # Create the RFC 2544 'metric test
395 if args.metric == "throughput":
397 _LOGGER.debug("Set up the RFC2544 throughput test...")
398 stc.perform("Rfc2544SetupThroughputTestCommand",
399 params={"AcceptableFrameLoss":
400 args.acceptable_frame_loss_pct,
401 "Duration": args.trial_duration_sec,
402 "FrameSizeList": args.frame_size_list,
403 "LearningMode": args.learning_mode,
404 "NumOfTrials": args.num_trials,
405 "RateInitial": args.rate_initial_pct,
406 "RateLowerLimit": args.rate_lower_limit_pct,
407 "RateStep": args.rate_step_pct,
408 "RateUpperLimit": args.rate_upper_limit_pct,
409 "Resolution": args.resolution_pct,
410 "SearchMode": args.search_mode,
411 "TrafficPattern": args.traffic_pattern})
412 elif args.metric == "backtoback":
413 stc.perform("Rfc2544SetupBackToBackTestCommand",
414 params={"AcceptableFrameLoss":
415 args.acceptable_frame_loss_pct,
416 "Duration": args.trial_duration_sec,
417 "FrameSizeList": args.frame_size_list,
418 "LearningMode": args.learning_mode,
419 "LatencyType": args.latency_type,
420 "NumOfTrials": args.num_trials,
421 "RateInitial": args.rate_initial_pct,
422 "RateLowerLimit": args.rate_lower_limit_pct,
423 "RateStep": args.rate_step_pct,
424 "RateUpperLimit": args.rate_upper_limit_pct,
425 "Resolution": args.resolution_pct,
426 "SearchMode": args.search_mode,
427 "TrafficPattern": args.traffic_pattern})
428 elif args.metric == "frameloss":
429 stc.perform("Rfc2544SetupFrameLossTestCommand",
430 params={"AcceptableFrameLoss":
431 args.acceptable_frame_loss_pct,
432 "Duration": args.trial_duration_sec,
433 "FrameSizeList": args.frame_size_list,
434 "LearningMode": args.learning_mode,
435 "LatencyType": args.latency_type,
436 "NumOfTrials": args.num_trials,
437 "RateInitial": args.rate_initial_pct,
438 "RateLowerLimit": args.rate_lower_limit_pct,
439 "RateStep": args.rate_step_pct,
440 "RateUpperLimit": args.rate_upper_limit_pct,
441 "Resolution": args.resolution_pct,
442 "SearchMode": args.search_mode,
443 "TrafficPattern": args.traffic_pattern})
444 elif args.metric == "latency":
445 stc.perform("Rfc2544SetupLatencyTestCommand",
446 params={"AcceptableFrameLoss":
447 args.acceptable_frame_loss_pct,
448 "Duration": args.trial_duration_sec,
449 "FrameSizeList": args.frame_size_list,
450 "LearningMode": args.learning_mode,
451 "LatencyType": args.latency_type,
452 "NumOfTrials": args.num_trials,
453 "RateInitial": args.rate_initial_pct,
454 "RateLowerLimit": args.rate_lower_limit_pct,
455 "RateStep": args.rate_step_pct,
456 "RateUpperLimit": args.rate_upper_limit_pct,
457 "Resolution": args.resolution_pct,
458 "SearchMode": args.search_mode,
459 "TrafficPattern": args.traffic_pattern})
461 # Save the configuration
462 stc.perform("SaveToTcc", params={"Filename": "2544.tcc"})
463 # Connect to the hardware...
464 stc.perform("AttachPorts", params={"portList": stc.get(
465 "system1.project", "children-port"), "autoConnect": "TRUE"})
466 # Apply configuration.
468 _LOGGER.debug("Apply configuration...")
472 _LOGGER.debug("Starting the sequencer...")
473 stc.perform("SequencerStart")
475 # Wait for sequencer to finish
477 "Starting test... Please wait for the test to complete...")
478 stc.wait_until_complete()
479 _LOGGER.info("The test has completed... Saving results...")
481 # Determine what the results database filename is...
482 lab_server_resultsdb = stc.get(
483 "system1.project.TestResultSetting", "CurrentResultFileName")
486 _LOGGER.debug("The lab server results database is %s",
487 lab_server_resultsdb)
489 stc.perform("CSSynchronizeFiles",
490 params={"DefaultDownloadDir": args.results_dir})
492 resultsdb = args.results_dir + \
493 lab_server_resultsdb.split("/Results")[1]
495 if not os.path.exists(resultsdb):
496 resultsdb = lab_server_resultsdb
497 _LOGGER.info("Failed to create the local summary DB File, using"
498 " the remote DB file instead.")
501 "The local summary DB file has been saved to %s", resultsdb)
503 # The returns the "RFC2544ThroughputTestResultDetailedSummaryView"
504 # table view from the results database.
505 # There are other views available.
507 if args.metric == "throughput":
509 stc.perform("QueryResult",
511 "DatabaseConnectionString":
514 ("RFC2544ThroughputTestResultDetailed"
517 # The returns the "RFC2544BacktoBackTestResultDetailedSummaryView"
518 # table view from the results database.
519 # There are other views available.
520 elif args.metric == "backtoback":
522 stc.perform("QueryResult",
524 "DatabaseConnectionString":
527 ("RFC2544Back2BackTestResultDetailed"
530 # The returns the "RFC2544LatencyTestResultDetailedSummaryView"
531 # table view from the results database.
532 # There are other views available.
533 elif args.metric == "latency":
535 stc.perform("QueryResult",
537 "DatabaseConnectionString":
540 ("RFC2544LatencyTestResultDetailed"
543 # The returns the "RFC2544FrameLossTestResultDetailedSummaryView"
544 # table view from the results database.
545 # There are other views available.
546 elif args.metric == "frameloss":
548 stc.perform("QueryResult",
550 "DatabaseConnectionString":
553 ("RFC2544FrameLossTestResultDetailed"
556 _LOGGER.debug("resultsdict[\"Columns\"]: %s",
557 resultsdict["Columns"])
558 _LOGGER.debug("resultsdict[\"Output\"]: %s", resultsdict["Output"])
559 _LOGGER.debug("Result paths: %s",
560 stc.perform("GetTestResultSettingPaths"))
562 # Write results to csv
563 _LOGGER.debug("Writing CSV file to results directory %s",
565 write_query_results_to_csv(
566 args.results_dir, args.csv_results_file_prefix, resultsdict)
568 except RuntimeError as e:
572 _LOGGER.debug("Destroy session on lab server")
575 _LOGGER.info("Test complete!")
577 if __name__ == "__main__":