1 # Copyright (c) 2016-2019 Intel Corporation
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.
20 # ixload uses its own py2. So importing jsonutils fails. So adding below
21 # workaround to support call from yardstick
23 from oslo_serialization import jsonutils
25 import json as jsonutils
28 class ErrorClass(object):
30 def __init__(self, *args, **kwargs):
31 if 'test' not in kwargs:
34 def __getattr__(self, item):
38 class InvalidRxfFile(Exception):
39 message = 'Loaded rxf file has unexpected format'
43 from IxLoad import IxLoad, StatCollectorUtils
46 StatCollectorUtils = ErrorClass
48 LOG = logging.getLogger(__name__)
49 CSV_FILEPATH_NAME = 'IxL_statResults.csv'
54 'L2-3 Stats for Client Ports.csv',
55 'L2-3 Stats for Server Ports.csv',
56 'IxLoad Detailed Report.html',
57 'IxLoad Detailed Report.pdf'
61 ["HTTP Client", "TCP Connections Established", "kSum"],
62 ["HTTP Client", "TCP Connection Requests Failed", "kSum"],
63 ["HTTP Client", "HTTP Simulated Users", "kSum"],
64 ["HTTP Client", "HTTP Concurrent Connections", "kSum"],
65 ["HTTP Client", "HTTP Connections", "kSum"],
66 ["HTTP Client", "HTTP Transactions", "kSum"],
67 ["HTTP Client", "HTTP Connection Attempts", "kSum"]
71 ["HTTP Server", "TCP Connections Established", "kSum"],
72 ["HTTP Server", "TCP Connection Requests Failed", "kSum"]
76 INCOMING_STAT_RECORD_TEMPLATE = """
77 =====================================
78 INCOMING STAT RECORD >>> %s
82 =====================================
85 INCOMING_STAT_INTERVAL_TEMPLATE = """
86 =====================================
87 Incoming stats: Time interval: %s
88 Incoming stats: Time interval: %s
89 =====================================
93 def validate_non_string_sequence(value, default=None, raise_exc=None):
94 if isinstance(value, collections.Sequence) and not isinstance(value, str):
97 raise raise_exc # pylint: disable=raising-bad-type
101 def join_non_strings(separator, *non_strings):
103 non_strings = validate_non_string_sequence(non_strings[0], raise_exc=RuntimeError)
104 except (IndexError, RuntimeError):
106 return str(separator).join(str(non_string) for non_string in non_strings)
109 class IXLOADHttpTest(object):
111 def __init__(self, test_input):
113 self.stat_utils = None
114 self.remote_server = None
115 self.config_file = None
116 self.results_on_windows = None
117 self.result_dir = None
120 self.ports_to_reassign = None
121 self.links_param = None
122 self.test_input = jsonutils.loads(test_input)
123 self.parse_run_test()
127 def format_ports_for_reassignment(ports):
128 formatted = [join_non_strings(';', p) for p in ports if len(p) == 3]
129 LOG.debug('for client ports:%s', os.linesep.join(formatted))
132 def reassign_ports(self, test, repository, ports_to_reassign):
133 LOG.debug('ReassignPorts: %s %s', test, repository)
135 chassis_chain = repository.cget('chassisChain')
136 LOG.debug('chassischain: %s', chassis_chain)
137 client_ports = ports_to_reassign[0::2]
138 server_ports = ports_to_reassign[1::2]
140 client_ports = self.format_ports_for_reassignment(client_ports)
141 LOG.debug('Reassigning client ports: %s', client_ports)
142 server_ports = self.format_ports_for_reassignment(server_ports)
143 LOG.debug('Reassigning server ports: %s', server_ports)
144 ports_to_set = client_ports + server_ports
147 LOG.debug('Reassigning ports: %s', ports_to_set)
148 test.setPorts(ports_to_set)
150 LOG.error('Error: Could not remap port assignment for: %s',
152 self.ix_load.delete(repository)
153 self.ix_load.disconnect()
157 def stat_collector(*args):
158 LOG.debug(INCOMING_STAT_RECORD_TEMPLATE, args, len(args), args[0], args[1])
161 def IxL_StatCollectorCommand(*args):
163 timestamp = args[1][1]
164 LOG.debug(INCOMING_STAT_INTERVAL_TEMPLATE, timestamp, stats)
167 def set_results_dir(test_controller, results_on_windows):
169 If the folder doesn't exists on the Windows Client PC,
170 IxLoad will automatically create it.
173 test_controller.setResultDir(results_on_windows)
175 LOG.error('Error creating results dir on Win: %s',
179 def load_config_file(self, config_file):
181 LOG.debug(config_file)
182 repository = self.ix_load.new("ixRepository", name=config_file)
185 LOG.error('Error: IxLoad config file not found: %s', config_file)
188 def update_network_address(self, net_traffic, address, gateway, prefix):
189 """Update ip address and gateway for net_traffic object
191 This function update field which configure source addresses for
192 traffic which is described by net_traffic object.
193 Do not return anything
195 :param net_traffic: (IxLoadObjectProxy) proxy obj to tcl net_traffic object
196 :param address: (str) Ipv4 range start address
197 :param gateway: (str) Ipv4 address of gateway
198 :param prefix: (int) subnet prefix
202 ethernet = net_traffic.network.getL1Plugin()
203 ix_net_l2_ethernet_plugin = ethernet.childrenList[0]
204 ix_net_ip_v4_v6_plugin = ix_net_l2_ethernet_plugin.childrenList[0]
205 ix_net_ip_v4_v6_range = ix_net_ip_v4_v6_plugin.rangeList[0]
207 ix_net_ip_v4_v6_range.config(
210 gatewayAddress=gateway)
214 def update_network_mac_address(self, net_traffic, mac):
215 """Update MACaddress for net_traffic object
217 This function update field which configure MACaddresses for
218 traffic which is described by net_traffic object.
219 If mac == "auto" then will be configured auto generated mac
220 Do not return anything.
222 :param net_traffic: (IxLoadObjectProxy) proxy obj to tcl net_traffic object
223 :param mac: (str) MAC
227 ethernet = net_traffic.network.getL1Plugin()
228 ix_net_l2_ethernet_plugin = ethernet.childrenList[0]
229 ix_net_ip_v4_v6_plugin = ix_net_l2_ethernet_plugin.childrenList[0]
230 ix_net_ip_v4_v6_range = ix_net_ip_v4_v6_plugin.rangeList[0]
232 if str(mac).lower() == "auto":
233 ix_net_ip_v4_v6_range.config(autoMacGeneration=True)
235 ix_net_ip_v4_v6_range.config(autoMacGeneration=False)
236 mac_range = ix_net_ip_v4_v6_range.getLowerRelatedRange(
238 mac_range.config(mac=mac)
242 def update_network_param(self, net_traffic, param):
243 """Update net_traffic by parameters specified in param"""
245 self.update_network_address(net_traffic, param["address"],
246 param["gateway"], param["subnet_prefix"])
248 self.update_network_mac_address(net_traffic, param["mac"])
250 def update_config(self):
251 """Update some fields by parameters from traffic profile"""
254 # self.test.communityList is a IxLoadObjectProxy to some tcl object
255 # which contain all net_traffic objects in scenario.
256 # net_traffic item has a name in format "activity_name@item_name"
258 for item in self.test.communityList:
259 net_traffics[item.name.split('@')[1]] = item
260 except Exception: # pylint: disable=broad-except
263 for name, net_traffic in net_traffics.items():
265 param = self.links_param[name]
267 LOG.debug('There is no param for net_traffic %s', name)
270 self.update_network_param(net_traffic, param["ip"])
272 self.update_http_client_param(net_traffic, param["http_client"])
274 def update_http_client_param(self, net_traffic, param):
275 """Update http client object in net_traffic
277 Update http client object in net_traffic by parameters
279 Do not return anything.
281 :param net_traffic: (IxLoadObjectProxy) proxy obj to tcl net_traffic object
282 :param param: (dict) http_client section from traffic profile
285 page = param.get("page_object")
287 self.update_page_size(net_traffic, page)
288 users = param.get("simulated_users")
290 self.update_user_count(net_traffic, users)
292 def update_page_size(self, net_traffic, page_object):
293 """Update page_object field in http client object in net_traffic
295 This function update field which configure page_object
296 which will be loaded from server
297 Do not return anything.
299 :param net_traffic: (IxLoadObjectProxy) proxy obj to tcl net_traffic object
300 :param page_object: (str) path to object on server e.g. "/4k.html"
304 activity = net_traffic.activityList[0]
305 ix_http_command = activity.agent.actionList[0]
306 ix_http_command.config(pageObject=page_object)
310 def update_user_count(self, net_traffic, user_count):
311 """Update userObjectiveValue field in activity object in net_traffic
313 This function update field which configure users count
314 which will be simulated by client.
315 Do not return anything.
317 :param net_traffic: (IxLoadObjectProxy) proxy obj to tcl net_traffic object
318 :param user_count: (int) number of simulated users
322 activity = net_traffic.activityList[0]
323 activity.config(userObjectiveValue=user_count)
327 def start_http_test(self):
328 self.ix_load = IxLoad()
330 LOG.debug('--- ixLoad obj: %s', self.ix_load)
332 self.ix_load.connect(self.remote_server)
336 log_tag = "IxLoad-api"
338 logger = self.ix_load.new("ixLogger", log_tag, 1)
339 log_engine = logger.getEngine()
340 log_engine.setLevels(self.ix_load.ixLogger.kLevelDebug,
341 self.ix_load.ixLogger.kLevelInfo)
342 log_engine.setFile(log_name, 2, 256, 1)
344 # Initialize stat collection utilities
345 self.stat_utils = StatCollectorUtils()
347 test_controller = self.ix_load.new("ixTestController", outputDir=1)
349 repository = self.load_config_file(self.config_file)
351 # Get the first test on the testList
352 test_name = repository.testList[0].cget("name")
353 self.test = repository.testList.getItem(test_name)
355 self.set_results_dir(test_controller, self.results_on_windows)
357 self.test.config(statsRequired=1, enableResetPorts=1, csvInterval=2,
358 enableForceOwnership=True)
362 # ---- Remap ports ----
364 self.reassign_ports(self.test, repository, self.ports_to_reassign)
365 except Exception: # pylint: disable=broad-except
366 LOG.exception("Exception occurred during reassign_ports")
368 # -----------------------------------------------------------------------
369 # Set up stat Collection
370 # -----------------------------------------------------------------------
371 test_server_handle = test_controller.getTestServerHandle()
372 self.stat_utils.Initialize(test_server_handle)
374 # Clear any stats that may have been registered previously
375 self.stat_utils.ClearStats()
377 # Define the stats we would like to collect
378 self.stat_utils.AddStat(caption="Watch_Stat_1",
379 statSourceType="HTTP Client",
380 statName="TCP Connections Established",
381 aggregationType="kSum",
384 self.stat_utils.AddStat(caption="Watch_Stat_2",
385 statSourceType="HTTP Client",
386 statName="TCP Connection Requests Failed",
387 aggregationType="kSum",
390 self.stat_utils.AddStat(caption="Watch_Stat_3",
391 statSourceType="HTTP Server",
392 statName="TCP Connections Established",
393 aggregationType="kSum",
396 self.stat_utils.AddStat(caption="Watch_Stat_4",
397 statSourceType="HTTP Server",
398 statName="TCP Connection Requests Failed",
399 aggregationType="kSum",
402 self.stat_utils.StartCollector(self.IxL_StatCollectorCommand)
404 test_controller.run(self.test)
405 self.ix_load.waitForTestFinish()
407 test_controller.releaseConfigWaitFinish()
409 # Stop the collector (running in the tcl event loop)
410 self.stat_utils.StopCollector()
413 test_controller.generateReport(detailedReport=1, format="PDF;HTML")
414 test_controller.releaseConfigWaitFinish()
416 self.ix_load.delete(self.test)
417 self.ix_load.delete(test_controller)
418 self.ix_load.delete(logger)
419 self.ix_load.delete(log_engine)
421 LOG.debug('Retrieving CSV stats from Windows Client PC ...')
422 for stat_file in STATS_TO_GET:
423 enhanced_stat_file = stat_file.replace('-', '')
424 enhanced_stat_file = enhanced_stat_file.replace(' ', '_')
425 enhanced_stat_file = enhanced_stat_file.replace('__', '_')
427 LOG.debug('Getting csv stat file: %s', stat_file)
428 src_file = os.path.join(self.results_on_windows, stat_file)
429 dst_file = os.path.join(self.result_dir, '_'.join(['ixLoad', enhanced_stat_file]))
430 self.ix_load.retrieveFileCopy(src_file, dst_file)
432 self.ix_load.disconnect()
434 def parse_run_test(self):
435 self.remote_server = self.test_input["remote_server"]
436 LOG.debug("remote tcl server: %s", self.remote_server)
438 self.config_file = self.test_input["ixload_cfg"]
439 LOG.debug("ixload config: %s", self.remote_server)
441 self.results_on_windows = 'C:/Results'
442 self.result_dir = self.test_input["result_dir"]
443 self.chassis = self.test_input["ixia_chassis"]
444 LOG.debug("remote ixia chassis: %s", self.chassis)
446 self.card = self.test_input["IXIA"]["card"]
447 self.ports_to_reassign = [
448 [self.chassis, self.card, port] for port in
449 self.test_input["IXIA"]["ports"]
452 LOG.debug("Ports to be reassigned: %s", self.ports_to_reassign)
454 self.links_param = self.test_input["links_param"]
455 LOG.debug("Links param to be applied: %s", self.links_param)
459 # Get the args from cmdline and parse and run the test
460 test_input = "".join(args[1:])
462 ixload_obj = IXLOADHttpTest(test_input)
463 ixload_obj.start_http_test()
465 if __name__ == '__main__':
466 LOG.info("Start http_ixload test")