def get_parsed_data(self, input, delimiter=None): ''' @param raw_data output from the CLI execution result @param delimiter character to split the key and value @rtype: dict @return: calling the get_parsed_data function will return a hash based on above sample, the return data will be: {'hwaddr': '00:50:56:a4:ca:ea', 'ip4': ['172.16.55.1', '172.16.56.3', '127.0.0.1'], 'ip6': ['fe80::250:56ff:fea4:ac', 'fe80::250:56ff:fea4:accf'], 'vnic_state': 'up'} ''' if delimiter is None: delimiter = self.DEFAULT_DELIMITER # TODO: Should be moving the regex to common regexutils library # tested 'inet%s' % regex_utils.ip but its not working ip4_regex = "inet\s+(\d+\.\d+\.\d+\.\d+)" list_of_ip4_ips = re.findall(ip4_regex, input, re.IGNORECASE) # TODO: Should be moving the regex to common regexutils library ip6_regex = "inet6\s+([\w+,':']*)" list_of_ip6_ips = re.findall(ip6_regex, input, re.IGNORECASE) # get mac address mac_regex = "HWaddr:\s+(([\w]{2}[:-]){5}([\w]{2}))" match = re.search(mac_regex, input, re.IGNORECASE) if match: mac = match.group(1) else: pylogger.warn("No match found for hwaddr address. Using None.") mac = None # Interface Status Regex interface_status_regex = "Interface.*is\s+(up|down)," match = re.search(interface_status_regex, input, re.IGNORECASE) if match: vnic_state = match.group(1) else: pylogger.warn("No match found for vnic_state. Using None.") vnic_state = None default_list_val = [] default_str_val = '' py_dict = {} import vmware.common.utilities as utilities py_dict[self.IP4_KEY] = utilities.get_default(list_of_ip4_ips, default_list_val) py_dict[self.IP6_KEY] = utilities.get_default(list_of_ip6_ips, default_list_val) py_dict[self.MAC_ADDR_KEY] = utilities.get_default(mac, default_str_val) py_dict[self.VNIC_STATE_KEY] = utilities.get_default(vnic_state, default_str_val) return py_dict
def get_parsed_data(self, input, delimiter=None): ''' @param raw_data output from the CLI execution result @param delimiter character to split the key and value @rtype: dict @return: calling the get_parsed_data function will return a hash based on above sample, the return data will be: {'hwaddr': '00:50:56:a4:ca:ea', 'ip4': ['172.16.55.1', '172.16.56.3', '127.0.0.1'], 'ip6': ['fe80::250:56ff:fea4:ac', 'fe80::250:56ff:fea4:accf'], 'vnic_state': 'up'} ''' if delimiter is None: delimiter = self.DEFAULT_DELIMITER # TODO: Should be moving the regex to common regexutils library # tested 'inet%s' % regex_utils.ip but its not working ip4_regex = "inet\s+(\d+\.\d+\.\d+\.\d+)" list_of_ip4_ips = re.findall(ip4_regex, input, re.IGNORECASE) # TODO: Should be moving the regex to common regexutils library ip6_regex = "inet6\s+([\w+,':']*)" list_of_ip6_ips = re.findall(ip6_regex, input, re.IGNORECASE) # get mac address mac_regex = "HWaddr:\s+(([\w]{2}[:-]){5}([\w]{2}))" match = re.search(mac_regex, input, re.IGNORECASE) if match: mac = match.group(1) else: pylogger.warn("No match found for hwaddr address. Using None.") mac = None # Interface Status Regex interface_status_regex = "Interface.*is\s+(up|down)," match = re.search(interface_status_regex, input, re.IGNORECASE) if match: vnic_state = match.group(1) else: pylogger.warn("No match found for vnic_state. Using None.") vnic_state = None default_list_val = [] default_str_val = '' py_dict = {} import vmware.common.utilities as utilities py_dict[self.IP4_KEY] = utilities.get_default(list_of_ip4_ips, default_list_val) py_dict[self.IP6_KEY] = utilities.get_default(list_of_ip6_ips, default_list_val) py_dict[self.MAC_ADDR_KEY] = utilities.get_default( mac, default_str_val) py_dict[self.VNIC_STATE_KEY] = utilities.get_default( vnic_state, default_str_val) return py_dict
def _write_ifcfg_bridge(cls, client_object, device, ovs_extra=None, ovs_dhcp_interfaces=None, ovs_boot_proto=None): ovs_extra = utilities.get_default(ovs_extra, '') ovs_dhcp_interfaces = utilities.get_default(ovs_dhcp_interfaces, '') ovs_boot_proto = utilities.get_default(ovs_boot_proto, 'none') config_map = ovs_helper.IFCFG_BRIDGE_MAP config_map['DEVICE'] = device config_map['OVS_EXTRA'] = ovs_extra config_map['OVSBOOTPROTO'] = ovs_boot_proto config_map['OVSDHCPINTERFACES'] = ovs_dhcp_interfaces config = ['%s="%s"' % (key, val) for key, val in config_map.iteritems()] return Linux.create_file( client_object, cls.IFCFG_FILE % device, content='\n'.join(config), overwrite=True)
def start_netcat_server(cls, client_object, ip=None, port=None, udp=None, wait=None): """ Starts a listening process for inbound connections. @type client_object: VMCMDClient @param client_object: Client object @type ip: string @param ip: IP address to bind the listening process. @type port: integer @param port: port to start the listening on. @type udp: boolean @param udp: Use UDP instead of the default option of TCP @type wait: boolean @param wait: If True, run cmd as blocking call, else as non-blocking @rtype: vmware.common.result.Result object @return: result object. """ wait = utilities.get_default(wait, True) cmd = 'nc -l' if ip: cmd = '%s -s %s' % (cmd, ip) if port: cmd = '%s -p %s' % (cmd, int(port)) if udp: cmd = '%s -u' % cmd if not wait: cmd = "%s >> /dev/null 2>&1 &" % cmd return client_object.connection.request(cmd)
def download_files(cls, client_object, resource=None, destination=None, timeout=None): """ Download files onto the host @type client_object: BaseClient @param client_object: Used to pass commands to the host. @type resource: string @param resource: file or url to a specific resource @type destination: string @param destination: destination directory @rtype: status_code @return: SUCCESS or FAILURE """ timeout = utilities.get_default(timeout, 120) pylogger.info("Downloading files onto host") timestamp = time.time() dest_tmp = '/%s/%d' % (destination, int(timestamp)) cmd = 'mkdir %s' % dest_tmp try: client_object.connection.request(cmd, strict=False) except Exception as e: pylogger.exception("could not make a tmp dir", e) raise for source in resource: command = 'wget -P %s %s' % (dest_tmp, source) pylogger.debug(command) try: client_object.connection.request(command, timeout=timeout) except Exception, error: pylogger.exception(error) error.status_code = common.status_codes.RUNTIME_ERROR raise
def start_netcat_server(cls, client_object, ip=None, port=None, udp=None, wait=None): """ Starts a listening process for inbound connections. @type client_object: VMCMDClient @param client_object: Client object @type ip: string @param ip: IP address to bind the listening process. @type port: integer @param port: port to start the listening on. @type udp: boolean @param udp: Use UDP instead of the default option of TCP @type wait: boolean @param wait: If True, run cmd as blocking call, else as non-blocking @rtype: vmware.common.result.Result object @return: result object. """ wait = utilities.get_default(wait, True) cmd = 'nc -l' if ip: cmd = '%s -s %s' % (cmd, ip) if port: cmd = '%s -p %s' % (cmd, int(port)) if udp: cmd = '%s -u' % cmd if not wait: cmd = "%s >> /dev/null 2>&1 &" % cmd return client_object.connection.request(cmd)
def __init__(self, ip="", username="", password="", root_password="", connection_object=None, terminal_prompts=None): super(ExpectConnection, self).\ __init__(ip, username, password, root_password, connection_object) self.terminal_prompts = utilities.get_default(terminal_prompts, TERMINAL_PROMPTS)
def disconnect_vdr_port_from_switch(cls, client_object, switch_name): """ Disconnect VDR port from logical switch. """ switch_name = utilities.get_default(switch_name, 'nsxvswitch') command = "net-vdr --connection -d -s %s" % switch_name try: client_object.connection.request(command) except Exception, error: pylogger.error("Exception thrown when disconnecting vdr port: %r" % error) raise
def _write_ifcfg_bridge(cls, client_object, device, ovs_extra=None, ovs_dhcp_interfaces=None, ovs_boot_proto=None): ovs_extra = utilities.get_default(ovs_extra, '') ovs_dhcp_interfaces = utilities.get_default(ovs_dhcp_interfaces, '') ovs_boot_proto = utilities.get_default(ovs_boot_proto, 'none') config_map = ovs_helper.IFCFG_BRIDGE_MAP config_map['DEVICE'] = device config_map['OVS_EXTRA'] = ovs_extra config_map['OVSBOOTPROTO'] = ovs_boot_proto config_map['OVSDHCPINTERFACES'] = ovs_dhcp_interfaces config = [ '%s="%s"' % (key, val) for key, val in config_map.iteritems() ] return Linux.create_file(client_object, cls.IFCFG_FILE % device, content='\n'.join(config), overwrite=True)
def disconnect_vdr_port_from_switch(cls, client_object, switch_name): """ Disconnect VDR port from logical switch. """ switch_name = utilities.get_default(switch_name, 'nsxvswitch') command = "net-vdr --connection -d -s %s" % switch_name try: client_object.connection.request(command) except Exception, error: pylogger.error("Exception thrown when disconnecting vdr port: %r" % error) raise
def delete_vdr_port(cls, client_object, switch_name): """ Delete VDR port from logical switch. """ switch_name = utilities.get_default(switch_name, 'nsxvswitch') command = "net-dvs -D -p vdrPort %s" % switch_name try: client_object.connection.request(command) except Exception, error: pylogger.error("Failed to delete vdr port from %s: %r" % (switch_name, error)) raise
def get_url_parameters(cls, http_verb, **kwargs): """ Returns URL query parameters as a dictionary. Consumes 'query_params' from **kwargs dict. @type query_params: dict @param query_params: Key-value map of URL query parameters. @rtype: dict @return: A key-value map with all keys and values stringified. """ query_params = utilities.get_default(kwargs.pop('query_params', None), {}) return {str(k): str(v) for k, v in query_params.iteritems()}
def get_url_parameters(cls, http_verb, **kwargs): """ Returns URL query parameters as a dictionary. Consumes 'query_params' from **kwargs dict. @type query_params: dict @param query_params: Key-value map of URL query parameters. @rtype: dict @return: A key-value map with all keys and values stringified. """ query_params = utilities.get_default(kwargs.pop('query_params', None), {}) return {str(k): str(v) for k, v in query_params.iteritems()}
def install(cls, client_object, resource=None, timeout=None, ordered=None): """ Fetches the packages in a local directory and then installs them. @type client_object: BaseClient @param client_object: Used to pass commands to the host. @type resource: list @param resource: List of URLs to packages. @type ordered: str @param ordered: When True, the packages are installed in order as specified in the list. If the user specifies a remote directory then any package within that directory would still be installed based on dependencies as determined by package manager. @type timeout: int @param timeout: Time after which the attempt to install packages is aborted. @rtype: NonType @return: None """ utilities.validate_list(resource) date_and_time = utilities.current_date_time() package_dir = os.sep.join([client_object.TMP_DIR, date_and_time]) ordered = utilities.get_default(ordered, "false") try: if ordered.lower() == "false": # Let package manager resolve the dependencies. cls._install(client_object, resource=resource, package_dir=package_dir, timeout=timeout) else: for package in resource: target_dir = filter(None, package.split('/'))[-1] sub_dir = os.sep.join([package_dir, target_dir]) cls._install(client_object, resource=package, package_dir=sub_dir, timeout=timeout) # Remove the downloaded packages. finally: client_object.connection.request(command='rm -r %s' % package_dir, timeout=timeout)
def install(cls, client_object, resource=None, timeout=None, ordered=None): """ Fetches the packages in a local directory and then installs them. @type client_object: BaseClient @param client_object: Used to pass commands to the host. @type resource: list @param resource: List of URLs to packages. @type ordered: str @param ordered: When True, the packages are installed in order as specified in the list. If the user specifies a remote directory then any package within that directory would still be installed based on dependencies as determined by package manager. @type timeout: int @param timeout: Time after which the attempt to install packages is aborted. @rtype: NonType @return: None """ utilities.validate_list(resource) date_and_time = utilities.current_date_time() package_dir = os.sep.join([client_object.TMP_DIR, date_and_time]) ordered = utilities.get_default(ordered, "false") try: if ordered.lower() == "false": # Let package manager resolve the dependencies. cls._install( client_object, resource=resource, package_dir=package_dir, timeout=timeout) else: for package in resource: target_dir = filter(None, package.split('/'))[-1] sub_dir = os.sep.join([package_dir, target_dir]) cls._install( client_object, resource=package, package_dir=sub_dir, timeout=timeout) # Remove the downloaded packages. finally: client_object.connection.request(command='rm -r %s' % package_dir, timeout=timeout)
def download_files(cls, client_object, resource=None, destination=None, timeout=None): """ Download files onto the host @type client_object: BaseClient @param client_object: Used to pass commands to the host. @type resource: string @param resource: file or url to a specific resource @type destination: string @param destination: destination directory @rtype: status_code @return: SUCCESS or FAILURE """ timeout = utilities.get_default(timeout, 120) pylogger.info("Downloading files onto host") timestamp = time.time() dest_tmp = '/%s/%d' % (destination, int(timestamp)) cmd = 'mkdir %s' % dest_tmp try: client_object.connection.request(cmd, strict=False) except Exception as e: pylogger.exception("could not make a tmp dir", e) raise for source in resource: command = 'wget -P %s %s' % (dest_tmp, source) pylogger.debug(command) try: client_object.connection.request(command, timeout=timeout) except Exception, error: pylogger.exception(error) error.status_code = common.status_codes.RUNTIME_ERROR raise
def request(self, command, strict=True, timeout=None, wait_for_reboot=None, reboot_timeout=None): """ Method for passing on request over SSH to the remote host. @param command: Command to be executed on the remote host. @type command: str @param strict: When set, method will raise an exception if the command executed on the host hasn't succeeded. The raising of exception is made optional so as to facilitate negative testing. @type wait_for_reboot: bool @param wait_for_reboot: Parameter to indicate that a command is going to reboot the system, when that happens paramiko will wait till the host reboots and will recreate a new session to the host so that next executed command doesn't fail. @type reboot_timeout: int @param reboot_timeout: Seconds to wait before timing out the wait on the rebooting host. @rtype: result.Result @return: Returns a result object that contains status_code, error, reason and response_data """ print "command before stripping: %s" % command command = command.strip('\n'); print "command after stripping: %s" % command reboot_timeout = utilities.get_default( reboot_timeout, constants.Timeout.HOST_REBOOT_MAX) timeout = utilities.get_default(timeout, COMMAND_EXEC_TIMEOUT) pylogger.debug("Executing command %r on %r with timeout of %r " "seconds" % (command, self.ip, timeout)) print "Executing command %r on %r with timeout of %r seconds" % (command, self.ip, timeout) print "Executing command s %s on %s with timeout of %s seconds" % (repr(command), self.ip, timeout) if command == "esxcli network nic DOWN -n vmnic1": command = command.lower() command = "/bin/"+command stdin, stdout, stderr = self.anchor.exec_command(str(command)) else: stdin, stdout, stderr = self.anchor.exec_command(command) # stdin, stdout, stderr = self.anchor.exec_command("""esxcli network nic up -n vmnic3""") stdin.flush() stdin.channel.shutdown_write() stdout.channel.settimeout(timeout) stderr.channel.settimeout(timeout) stderr_lines = stderr.read() stdout_lines = stdout.read() # All returned objects contain the same status code. status_code = stdout.channel.recv_exit_status() for line in stdout_lines.splitlines(): pylogger.debug("Stdout: %r" % line) for line in stderr_lines.splitlines(): if line: logger = (pylogger.error if status_code and strict else pylogger.debug) logger("Stderr: %r" % line) if strict and status_code: raise RuntimeError( 'SSH command error: Host=%r, command=%r, exitcode=%r, ' 'stdout=%r, stderr=%r' % (self.ip, command, status_code, stdout_lines, stderr_lines)) ret = result.Result() ret.response_data = stdout_lines ret.error = stderr_lines ret.status_code = status_code if wait_for_reboot: self.close() reboot_completed = compute_utilities.wait_for_ip_reachable( self.ip, timeout=reboot_timeout) if not reboot_completed: raise AssertionError("Waited %r seconds for %r to come back " "up but failed" % (reboot_timeout, self.ip)) self.create_connection() return ret
def get_parsed_data(self, raw_data, delimiter=None): ''' @type raw_data: str @param raw_data: output from the CLI execution result @type delimiter: str @param delimiter: character to split the key and value @rtype: dict @return: calling the get_parsed_data function will return a hash based on above sample, the return data ''' default_list_val = [] default_str_val = '' py_dict = {} sessions = [] if delimiter is None: delimiter = self.DEFAULT_DELIMITER ha_status_regex = "Highavailability Status:\s+(\w+)" ha_status_result = re.search(ha_status_regex, raw_data, re.IGNORECASE) if ha_status_result: ha_status = ha_status_result.group(1) else: pylogger.warn("Failed to find Highavailability Status") ha_status = None ha_unit_state_regex = "Highavailability Unit State:\s+(\w+)" ha_unit_state_result = re.search(ha_unit_state_regex, raw_data, re.IGNORECASE) if ha_unit_state_result: ha_unit_state = ha_unit_state_result.group(1) else: pylogger.warn("Failed to find Highavailability Unit State") ha_unit_state = None ha_admin_state_regex = "Highavailability Admin State:\s+(\w+)" ha_admin_state_result = re.search(ha_admin_state_regex, raw_data, re.IGNORECASE) if ha_admin_state_result: ha_admin_state = ha_admin_state_result.group(1) else: pylogger.warn("Failed to find Highavailability Admin State") ha_admin_state = None frequency_regex = "Frequency:\s+(\w+)" frequency_result = re.search(frequency_regex, raw_data, re.IGNORECASE) if frequency_result: frequency = frequency_result.group(1) else: pylogger.warn("Failed to find Frequency Value") frequency = None deadtime_regex = "Deadtime:\s+(\w+)" deadtime_result = re.search(deadtime_regex, raw_data, re.IGNORECASE) if deadtime_result: deadtime = deadtime_result.group(1) else: pylogger.warn("Failed to find Deadtime Value") deadtime = None routing_status_channel_regex = "Routing Status Channel:\s+(\w+)" routing_status_channel_result = re.search(routing_status_channel_regex, raw_data, re.IGNORECASE) if routing_status_channel_result: routing_status_channel = routing_status_channel_result.group(1) else: pylogger.warn("Failed to find Routing Status Channel") routing_status_channel = None routing_status_regex = "Routing Status:\s+(\w+)" routing_status_result = re.search(routing_status_regex, raw_data, re.IGNORECASE) if routing_status_result: routing_status = routing_status_result.group(1) else: pylogger.warn("Failed to find Routing Status") routing_status = None healthcheck_config_channel_regex = "Healthcheck Config " \ "Channel:\s+(\w+)" healthcheck_config_channel_result = re.search( healthcheck_config_channel_regex, raw_data, re.IGNORECASE) if healthcheck_config_channel_result: healthcheck_config_channel = \ healthcheck_config_channel_result.group(1) else: pylogger.warn("Failed to find Healthcheck Config Channel status") healthcheck_config_channel = None healthcheck_status_channel_regex = \ "Healthcheck Status Channel:\s+(\w+)" healthcheck_status_channel_result = re.search( healthcheck_status_channel_regex, raw_data, re.IGNORECASE) if healthcheck_status_channel_result: healthcheck_status_channel = \ healthcheck_status_channel_result.group(1) else: pylogger.warn("Failed to find Healthcheck Status Channel") healthcheck_status_channel = None session_regex = "Session\s+via\s+.*\s+" \ "(\d+\.\d+\.\d+\.\d+:\d+\.\d+\.\d+\.\d+.*)" list_sessions = re.findall(session_regex, raw_data, re.IGNORECASE) temp_sessions = [re.sub("\s+", " ", x).replace(":", " ").lower(). split(' ') for x in list_sessions] for x in temp_sessions: sessions.append(dict(zip(('from_ip', 'to_ip', 'status'), tuple(x)))) import vmware.common.utilities as utilities py_dict['ha_status'] = utilities.get_default(ha_status, default_str_val) py_dict['ha_unit_state'] = utilities.get_default(ha_unit_state, default_str_val) py_dict['ha_admin_state'] = utilities.get_default(ha_admin_state, default_str_val) py_dict['frequency'] = utilities.get_default(frequency, default_str_val) py_dict['deadtime'] = utilities.get_default(deadtime, default_str_val) py_dict['routing_status_channel'] = utilities.get_default( routing_status_channel, default_str_val) py_dict['routing_status'] = utilities.get_default(routing_status, default_str_val) py_dict['healthcheck_config_channel'] = utilities.get_default( healthcheck_config_channel, default_str_val) py_dict['healthcheck_status_channel'] = utilities.get_default( healthcheck_status_channel, default_str_val) py_dict['sessions'] = utilities.get_default(sessions, default_list_val) return py_dict
def query_file(cls, client_object, file_name=None, grep_after=None, grep_string=None, max_wait=None, interval=None, pattern=None, count=None): """ Repeatedly look in a file until the grep_string string appears, which must be found after the grep_after string @type client_object: BaseClient @param client_object: Used to pass commands to the host. @type file_name: string @param file_name: the file name to which you wish to find a string @type grep_after: string @param grep_after: the context string that needs to be found first @type grep_string: string @param grep_string: the context string that needs to be found second @type max_wait: integer @param max_wait: the maximum number of seconds to wait for string @type interval: integer @param interval: seconds between intervals @type pattern: string @param pattern: regex used to search for pattern in a file @type count: integer @param count: number of occurrences of pattern to be found @rtype: status_code @return: SUCCESS or FAILURE """ max_wait = utilities.get_default(max_wait, 30) interval = utilities.get_default(interval, 5) pylogger.debug("Waiting up to " + str(max_wait) + " seconds for " + "\"%s\" to show up in the file: %s" % (grep_string, file_name)) # TODO:Use the vmware.common.timeouts instead while max_wait > 0: if count is not None and pattern is not None: try: result = cls.find_pattern_count(client_object, file_name=file_name, start_str=grep_after, end_str=grep_string, pattern=pattern) if count == result: return common.status_codes.SUCCESS except Exception, error: pylogger.error("Failed in find_pattern_count: %s" % error) raise else: try: result = cls.file_find_context(client_object, file_name=file_name, start_str=grep_after, end_str=grep_string) except Exception, error: pylogger.exception("Failed to find context in the file: " "%s" % error) pylogger.debug("failed to find context, " "retry before timeout") if str(common.status_codes.SUCCESS) in result: return common.status_codes.SUCCESS
def query_file(cls, client_object, file_name=None, grep_after=None, grep_string=None, max_wait=None, interval=None, pattern=None, count=None): """ Repeatedly look in a file until the grep_string string appears, which must be found after the grep_after string @type client_object: BaseClient @param client_object: Used to pass commands to the host. @type file_name: string @param file_name: the file name to which you wish to find a string @type grep_after: string @param grep_after: the context string that needs to be found first @type grep_string: string @param grep_string: the context string that needs to be found second @type max_wait: integer @param max_wait: the maximum number of seconds to wait for string @type interval: integer @param interval: seconds between intervals @type pattern: string @param pattern: regex used to search for pattern in a file @type count: integer @param count: number of occurrences of pattern to be found @rtype: status_code @return: SUCCESS or FAILURE """ max_wait = utilities.get_default(max_wait, 30) interval = utilities.get_default(interval, 5) pylogger.debug("Waiting up to " + str(max_wait) + " seconds for " + "\"%s\" to show up in the file: %s" % (grep_string, file_name)) while max_wait > 0: if count is not None and pattern is not None: try: result = cls.find_pattern_count(client_object, file_name=file_name, start_str=grep_after, end_str=grep_string, pattern=pattern) if count == result: return common.status_codes.SUCCESS except Exception, error: pylogger.error("Failed in find_pattern_count: %s" % error) raise else: try: result = cls.file_find_context(client_object, file_name=file_name, start_str=grep_after, end_str=grep_string) except Exception, error: pylogger.exception("Failed to find context in the file: " "%s" % error) pylogger.debug("failed to find context, " "retry before timeout") pass if "SUCCESS" in result: return common.status_codes.SUCCESS
def _get_iptable_cmd(cls, table=None, chain_action=None, chain=None, new_chain=None, rule_num=None, protocol=None, protocol_options=None, source=None, destination_ip=None, action=None, goto=None, in_interface=None, out_interface=None, fragment=None, set_counters=None, packets=None, bytes_=None, match_extensions=None, target=None, opts=None): """ Helper to get the iptables command. Arguments Notes: All arguments can be mapped one-one in arguments listed with commands in iptables man page. The only special options that can subsume other options in it are protocol_options and match_extensions. protocol_options only works when a protocol is specified. It is a map from the option related to that protocol and the value e.g. for protocol='tcp', protocol_options={'--dport': 5900} is valid. match_extensions is a map of maps, the key of the outer dict is the match extension module name e.g. 'comment' and 'icmp' etc. The keys of the inner dicts are the opts pertinent to that module and the corresponding value is the list of arguments that opt accepts. Note: The caller is responsible for passing in sane parameters and checking for any errors that may arise when executing the returned command. @type table: str @param table: Name of the table in which to insert the rule (e.g 'filter', 'nat', 'mangle', 'raw', 'security'). Defaults to 'filter'. @type chain_action: str @param aciton: Defines the chain_action to be performed on a chain (e.g. '-P' for setting a policy on the chain or '-E' to rename a chain) @type chain: str @param chain: Name of the chain on which chain_action will be performed (e.g. 'INPUT', 'FORWARD' or 'OUTPUT'). Defaults to 'INPUT'. @type new_chain: str @param new_chain: New name for the chain, if chain is being renamed. @type rule_num: str @param rule_num: Rule number @type protocol: str @param protocol: Protocol of the rule or packet to be checked. @type protocol_options: dict @param protocol_options: Map from opt to value that can be used to configure a rule (e.g. for tcp protocol, we can use {'--dport': 9999}) @type source: str @param source: Source IP address/mask for the rule. @type destination_ip: str @param destination_ip: Destination IP address/mask for the rule. @type action: str @param action: Decision for a matching packet (e.g. 'ACCEPT'). It can also be a user defined chain. @type goto: str @param goto: Name of the chain in which the processing should take place. @type in_interface: str @param in_interface: Name of the interface on which packet is received. @type out_interface: str @param out_interface: Name of the interface via which packet will be sent out. @type fragment: bool @param fragment: Creates rules for second and further packets for a fragmented packets. @type set_counters: bool @param set_counters: Flags if we need to set the counters. @type packets: int @param packets: The number to which packet counter needs to be set. @type bytes_: int @param bytes_: The number to which the byte counter needs to be set. @type match_extensions: dict @param match_extensions: Map of map, where the outer dict is used to index the extension module to load. The inner dict contains the key-value pair of options pertaining to that match extension module (e.g. {'comment': {'--comment': 'Doctest rule'}) @type target: str @param target: The policy target for a chain. @type opts: list @param opts: List of extra options that user wants to pass. @rtype: str @return: Returns the formatted iptables command. """ # TODO(Salman): Add support for negating the parameters. chain_action = utilities.get_default(chain_action, '-L') # Sanity checking. chain_actions = ('-A', '-D', '-I', '-R', '-P', '-E') new_chain_actions = ('-E',) rule_num_actions = ('-R',) not_rule_num_actions = ('-A', '-L', '-S', '-F') rule_spec_actions = ('-A',) not_rule_spec_actions = ('-L', '-S', '-F', '-Z', '-N', '-X', '-P', '-E') rule_num_or_rule_spec_actions = ('-D',) all_actions = set(chain_actions + new_chain_actions + rule_num_actions + not_rule_num_actions + rule_spec_actions + not_rule_spec_actions + rule_num_or_rule_spec_actions) rule_specifications = (protocol, source, destination_ip, action, goto, in_interface, out_interface, set_counters, protocol_options, match_extensions, target) rule_specified = filter(None, rule_specifications) if chain_action not in all_actions: raise ValueError('%r is not a valid chain_action' % chain_action) if chain_action in chain_actions and not chain: raise ValueError('Need to define a chain name with %r' % chain_action) if chain_action in rule_num_actions and not rule_num: raise ValueError('Need to define a rule number with %r' % chain_action) if chain_action in rule_spec_actions and not rule_specified: raise ValueError('Need to specify a rule with %r' % chain_action) rulenum_or_rule = ((rule_num and not rule_specified) or (rule_specified and not rule_num)) if ((chain_action in rule_num_or_rule_spec_actions and not rulenum_or_rule)): raise ValueError('Need to specify a rule number or rule ' 'specification, not both, with %r' % chain_action) if chain_action in not_rule_num_actions and rule_num: raise ValueError('%r command does not accept rule number' % chain_action) if chain_action in not_rule_spec_actions and rule_specified: raise ValueError('%r does not accept any rule specification, rule ' 'specification provided: %r' % (chain_action, rule_specifications)) table = utilities.get_default(table, cls.IPTABLE_FILTER_TABLE) chain = utilities.get_default(chain, cls.IPTABLE_INPUT_CHAIN) protocol_options = utilities.get_default(protocol_options, {}) cmd = ['iptables -t %s %s %s' % (table, chain_action, chain)] # Handle special cases where the second argument to be used with the # command can be different from rulenum and rule specifications. if chain_action == '-P': cmd.append(target) cmd.extend(opts) return ' '.join(cmd) if chain_action == '-E': cmd.append(new_chain) cmd.extend(opts) return ' '.join(cmd) if opts: cmd.extend(opts) if rule_num: cmd.append('%d' % rule_num) if protocol: cmd.append('-p %s' % protocol) if protocol_options and not protocol: protocol_options = {} if protocol_options: for opt, val in protocol_options.iteritems(): if opt == 'destination_port': cmd.append('--dport %s' % val) elif opt == 'source_port': cmd.append('--sport %s' % val) else: cmd.append('--%s %s' % (opt, val)) if match_extensions: for match_ext in match_extensions: if not match_ext.endswith('_match_ext'): raise ValueError('Need the match extension module names ' 'to end with "_match_ext", got %r' % match_ext) ext = ['-m %s' % match_ext.split("_match_ext")[0]] opts_dict = match_extensions[match_ext] opts_vals = [ '--%s "%s"' % (opt, ' '.join(utilities.as_list(val))) for opt, val in opts_dict.iteritems()] cmd.extend(ext + opts_vals) if source: cmd.append('-s %s' % source) if destination_ip: cmd.append('-d %s' % destination_ip) if action: cmd.append('-j %s' % action) if goto: cmd.append('-g %s' % goto) if in_interface: cmd.append('-i %s' % in_interface) if out_interface: cmd.append('-o %s' % out_interface) if fragment: cmd.append('-f') if set_counters: if None in (packets, bytes_): raise ValueError('set_counters is set but packets or bytes ' 'have not been provided') cmd.append('-c %s %s' % (packets, bytes_)) return ' '.join(cmd)