def delete_addr_and_conntrack_state(self, cidr): """Delete an address along with its conntrack state This terminates any active connections through an IP. :param cidr: the IP address for which state should be removed. This can be passed as a string with or without /NN. A netaddr.IPAddress or netaddr.Network representing the IP address can also be passed. """ self.addr.delete(cidr) ip_str = str(netaddr.IPNetwork(cidr).ip) ip_wrapper = IPWrapper(namespace=self.namespace) # Delete conntrack state for ingress traffic # If 0 flow entries have been deleted # conntrack -D will return 1 try: ip_wrapper.netns.execute(["conntrack", "-D", "-d", ip_str], check_exit_code=True, extra_ok_codes=[1]) except RuntimeError: LOG.exception(_LE("Failed deleting ingress connection state of" " floatingip %s"), ip_str) # Delete conntrack state for egress traffic try: ip_wrapper.netns.execute(["conntrack", "-D", "-q", ip_str], check_exit_code=True, extra_ok_codes=[1]) except RuntimeError: LOG.exception(_LE("Failed deleting egress connection state of" " floatingip %s"), ip_str)
def _read_stderr(self): data = self._read(self._process.stderr, self._stderr_lines) if self.log_output: LOG.error(_LE('Error received from [%(cmd)s]: %(err)s'), {'cmd': self.cmd, 'err': data}) if self.die_on_error: LOG.error(_LE("Process [%(cmd)s] dies due to the error: %(err)s"), {'cmd': self.cmd, 'err': data}) # the callback caller will use None to indicate the need to bail # out of the thread return None return data
def _sysctl(self, cmd): """execute() doesn't return the exit status of the command it runs, it returns stdout and stderr. Setting check_exit_code=True will cause it to raise a RuntimeError if the exit status of the command is non-zero, which in sysctl's case is an error. So we're normalizing that into zero (success) and one (failure) here to mimic what "echo $?" in a shell would be. This is all because sysctl is too verbose and prints the value you just set on success, unlike most other utilities that print nothing. execute() will have dumped a message to the logs with the actual output on failure, so it's not lost, and we don't need to print it here. """ cmd = ['sysctl', '-w'] + cmd ip_wrapper = IPWrapper(self.namespace) try: ip_wrapper.netns.execute(cmd, run_as_root=True, check_exit_code=True) except RuntimeError: LOG.exception(_LE("Failed running %s"), cmd) return 1 return 0
def _fork(self): try: pid = os.fork() if pid > 0: os._exit(0) except OSError: LOG.exception(_LE('Fork failed')) sys.exit(1)
def _validate_network_device_mtu(self): if (common.is_enabled() and self.conf.network_device_mtu < common.IPV6_MIN_MTU): LOG.error(_LE("IPv6 protocol requires a minimum MTU of " "%(min_mtu)s, while the configured value is " "%(current_mtu)s"), {'min_mtu': common.IPV6_MIN_MTU, 'current_mtu': self.conf.network_device_mtu}) raise SystemExit(1)
def _read_stderr(self): data = self._read(self._process.stderr, self._stderr_lines) if self.log_output: LOG.error(_LE('Error received from [%(cmd)s]: %(err)s'), { 'cmd': self.cmd, 'err': data }) if self.die_on_error: LOG.error(_LE("Process [%(cmd)s] dies due to the error: %(err)s"), { 'cmd': self.cmd, 'err': data }) # the callback caller will use None to indicate the need to bail # out of the thread return None return data
def unplug(self, device_name, bridge=None, namespace=None, prefix=None): """Unplug the interface.""" device = ip_lib.IPDevice(device_name, namespace=namespace) try: device.link.delete() LOG.debug("Unplugged interface '%s'", device_name) except RuntimeError: LOG.error(_LE("Failed unplugging interface '%s'"), device_name)
def __init__(self, pidfile, procname, uuid=None): self.pidfile = pidfile self.procname = procname self.uuid = uuid try: self.fd = os.open(pidfile, os.O_CREAT | os.O_RDWR) fcntl.flock(self.fd, fcntl.LOCK_EX | fcntl.LOCK_NB) except IOError: LOG.exception(_LE("Error while handling pidfile: %s"), pidfile) sys.exit(1)
def get_value_from_file(filename, converter=None): try: with open(filename, 'r') as f: try: return converter(f.read()) if converter else f.read() except ValueError: LOG.error(_LE('Unable to convert value in %s'), filename) except IOError: LOG.debug('Unable to access %s', filename)
def _exit_handler(self, uuid, service): """This is an exit handler for the ProcessMonitor. It will be called if the administrator configured the exit action in check_child_processes_actions, and one of our external processes die unexpectedly. """ LOG.error(_LE("Exiting agent because of a malfunction with the " "%(service)s process identified by uuid %(uuid)s"), {'service': service, 'uuid': uuid}) raise SystemExit(1)
def request_response(self, method, url, response): """ response is a modified HTTPResponse object or None. response.read() will not work on response as the underlying library request_eventlet.ApiRequestEventlet has already called this method in order to extract the body and headers for processing. ApiRequestEventlet derived classes call .read() and .getheaders() on the HTTPResponse objects and store the results in the response object's .body and .headers data members for future access. """ if response is None: # Timeout. LOG.error(_LE('Request timed out: %(method)s to %(url)s'), {'method': method, 'url': url}) raise exceptions.RequestTimeout() status = response.status LOG.debug("response.status = %(status)s", {'status': status}) if status == 401: raise exceptions.UnAuthorizedRequest() # Fail-fast: Check for exception conditions and raise the # appropriate exceptions for known error codes. if status in [404]: LOG.warning(_LW("Resource not found. Response status: %(status)s, " "response body: %(response.body)s"), {'status': status, 'response.body': response.body}) exceptions.ERROR_MAPPINGS[status](response) elif status in exceptions.ERROR_MAPPINGS: LOG.error(_LE("Received error code: %s"), status) LOG.error(_LE("Server Error Message: %s"), response.body) exceptions.ERROR_MAPPINGS[status](response) # Continue processing for non-error condition. if status != 200 and status != 201 and status != 204: LOG.error(_LE("%(method)s to %(url)s, unexpected response code: " "%(status)d (content = '%(body)s')"), {'method': method, 'url': url, 'status': response.status, 'body': response.body}) return None return self.request_response_body(response)
def start(self): """Start the daemon.""" if self.pidfile is not None and self.pidfile.is_running(): self.pidfile.unlock() LOG.error(_LE('Pidfile %s already exist. Daemon already ' 'running?'), self.pidfile) sys.exit(1) # Start the daemon self.daemonize() self.run()
def from_text(cls, line): route = line.split() try: first_word = route[0] except IndexError: with excutils.save_and_reraise_exception(): LOG.error(_LE('Unable to parse route "%s"'), line) added = (first_word != 'Deleted') if not added: route = route[1:] try: interface = ip_lib.remove_interface_suffix(route[1]) cidr = route[3] except IndexError: with excutils.save_and_reraise_exception(): LOG.error(_LE('Unable to parse route "%s"'), line) return cls(line, added, interface, cidr)
def unplug(self, device_name, bridge=None, namespace=None, prefix=None): """Unplug the interface.""" tap_name = self._get_tap_name(device_name, prefix) try: cmd = ['ivs-ctl', 'del-port', tap_name] utils.execute(cmd, run_as_root=True) device = ip_lib.IPDevice(device_name, namespace=namespace) device.link.delete() LOG.debug("Unplugged interface '%s'", device_name) except RuntimeError: LOG.error(_LE("Failed unplugging interface '%s'"), device_name)
def _arping(ns_name, iface_name, address, count): # Pass -w to set timeout to ensure exit if interface removed while running arping_cmd = ['arping', '-A', '-I', iface_name, '-c', count, '-w', 1.5 * count, address] try: ip_wrapper = IPWrapper(namespace=ns_name) ip_wrapper.netns.execute(arping_cmd, check_exit_code=True) except Exception: msg = _LE("Failed sending gratuitous ARP " "to %(addr)s on %(iface)s in namespace %(ns)s") LOG.exception(msg, {'addr': address, 'iface': iface_name, 'ns': ns_name})
def start(self): """Start the daemon.""" if self.pidfile is not None and self.pidfile.is_running(): self.pidfile.unlock() LOG.error( _LE('Pidfile %s already exist. Daemon already ' 'running?'), self.pidfile) sys.exit(1) # Start the daemon self.daemonize() self.run()
def _exit_handler(self, uuid, service): """This is an exit handler for the ProcessMonitor. It will be called if the administrator configured the exit action in check_child_processes_actions, and one of our external processes die unexpectedly. """ LOG.error( _LE("Exiting agent because of a malfunction with the " "%(service)s process identified by uuid %(uuid)s"), { 'service': service, 'uuid': uuid }) raise SystemExit(1)
def delete_addr_and_conntrack_state(self, cidr): """Delete an address along with its conntrack state This terminates any active connections through an IP. :param cidr: the IP address for which state should be removed. This can be passed as a string with or without /NN. A netaddr.IPAddress or netaddr.Network representing the IP address can also be passed. """ self.addr.delete(cidr) ip_str = str(netaddr.IPNetwork(cidr).ip) ip_wrapper = IPWrapper(namespace=self.namespace) # Delete conntrack state for ingress traffic # If 0 flow entries have been deleted # conntrack -D will return 1 try: ip_wrapper.netns.execute(["conntrack", "-D", "-d", ip_str], check_exit_code=True, extra_ok_codes=[1]) except RuntimeError: LOG.exception( _LE("Failed deleting ingress connection state of" " floatingip %s"), ip_str) # Delete conntrack state for egress traffic try: ip_wrapper.netns.execute(["conntrack", "-D", "-q", ip_str], check_exit_code=True, extra_ok_codes=[1]) except RuntimeError: LOG.exception( _LE("Failed deleting egress connection state of" " floatingip %s"), ip_str)
def request_response_body(response): if response and response.body: try: result = jsonutils.loads(response.body) LOG.debug("response.body = %(body)s", {'body': result}) return result['objects'] if 'objects' in result else result except UnicodeDecodeError: LOG.debug("The following strings cannot be decoded with " "'utf-8, trying 'ISO-8859-1' instead. %(body)s", {'body': response.body}) return jsonutils.loads(response.body, encoding='ISO-8859-1') except Exception as e: LOG.error(_LE("json decode error, the response.body %(body)s"), {'body': response.body}) return response.body return response
def _check_child_processes(self): # we build the list of keys before iterating in the loop to cover # the case where other threads add or remove items from the # dictionary which otherwise will cause a RuntimeError for service_id in list(self._monitored_processes): pm = self._monitored_processes.get(service_id) if pm and not pm.active: LOG.error(_LE("%(service)s for %(resource_type)s " "with uuid %(uuid)s not found. " "The process should not have died"), {'service': service_id.service, 'resource_type': self._resource_type, 'uuid': service_id.uuid}) self._execute_action(service_id) eventlet.sleep(0)
def format_cookie(cookie): if not cookie: return None try: fmt_headers = {} cookies = Cookie.SimpleCookie(cookie) for key, morsel in six.iteritems(cookies): if "ccsrftoken" in morsel.key: morsel.coded_value = morsel.value fmt_headers["X-CSRFTOKEN"] = morsel.value break fmt_headers["Cookie"] = cookies.output(header="").lstrip() return fmt_headers except (Cookie.CookieError, KeyError): LOG.error(_LE("The cookie ccsrftoken cannot be formatted")) raise Cookie.CookieError
def _kill_process(self, pid, kill_signal): try: # A process started by a root helper will be running as # root and need to be killed via the same helper. utils.execute(['kill', '-%d' % kill_signal, pid], run_as_root=self.run_as_root) except Exception as ex: stale_pid = (isinstance(ex, RuntimeError) and 'No such process' in str(ex)) if not stale_pid: LOG.exception(_LE('An error occurred while killing [%s].'), self.cmd) return False if self._process: self._process.wait() return True
def _arping(ns_name, iface_name, address, count): # Pass -w to set timeout to ensure exit if interface removed while running arping_cmd = [ 'arping', '-A', '-I', iface_name, '-c', count, '-w', 1.5 * count, address ] try: ip_wrapper = IPWrapper(namespace=ns_name) ip_wrapper.netns.execute(arping_cmd, check_exit_code=True) except Exception: msg = _LE("Failed sending gratuitous ARP " "to %(addr)s on %(iface)s in namespace %(ns)s") LOG.exception(msg, { 'addr': address, 'iface': iface_name, 'ns': ns_name })
def _check_child_processes(self): # we build the list of keys before iterating in the loop to cover # the case where other threads add or remove items from the # dictionary which otherwise will cause a RuntimeError for service_id in list(self._monitored_processes): pm = self._monitored_processes.get(service_id) if pm and not pm.active: LOG.error( _LE("%(service)s for %(resource_type)s " "with uuid %(uuid)s not found. " "The process should not have died"), { 'service': service_id.service, 'resource_type': self._resource_type, 'uuid': service_id.uuid }) self._execute_action(service_id) eventlet.sleep(0)
def _login(self, conn=None, headers=None): '''Issue login request and update authentication cookie.''' cookie = None g = eventlet_request.LoginRequestEventlet(self, self._user, self._password, conn, headers) g.start() ret = g.join() if ret: if isinstance(ret, Exception): LOG.error(_LE('Login error "%s"'), ret) raise ret cookie = ret.getheader("Set-Cookie") if cookie: LOG.debug("Saving new authentication cookie '%s'", cookie) return cookie
def _get_ip_link_output(cls): """Gets the output of the ip link help command Runs ip link help command and stores its output Note: ip link help return error and writes its output to stderr so we get the output from there. however, if this issue will be solved and the command will write to stdout, we will get the output from there too. """ try: ip_cmd = ['ip', 'link', 'help'] _stdout, _stderr = utils.execute(ip_cmd, check_exit_code=False, return_stderr=True, log_fail_as_error=False) except Exception as e: LOG.exception(_LE("Failed executing ip command")) raise UnsupportedIpLinkCommand(reason=e) return _stdout or _stderr
def _watch_process(self, callback, kill_event): while not kill_event.ready(): try: output = callback() if not output and output != "": break except Exception: LOG.exception(_LE('An error occurred while communicating ' 'with async process [%s].'), self.cmd) break # Ensure that watching a process with lots of output does # not block execution of other greenthreads. eventlet.sleep() # self._is_running being True indicates that the loop was # broken out of due to an error in the watched process rather # than the loop condition being satisfied. if self._is_running: self._is_running = False self._handle_process_error()
def _wait_for_login(self, conn, headers=None): '''Block until a login has occurred for the current API provider.''' data = self._get_provider_data(conn) if data is None: LOG.error(_LE("Login request for an invalid connection: '%s'"), utils.ctrl_conn_to_str(conn)) return provider_sem = data[0] if provider_sem.acquire(blocking=False): try: data = self._login(conn, headers) self.set_auth_data(conn, data) finally: provider_sem.release() else: LOG.debug("Waiting for auth to complete") # Wait until we can acquire then release provider_sem.acquire(blocking=True) provider_sem.release()
def _watch_process(self, callback, kill_event): while not kill_event.ready(): try: output = callback() if not output and output != "": break except Exception: LOG.exception( _LE('An error occurred while communicating ' 'with async process [%s].'), self.cmd) break # Ensure that watching a process with lots of output does # not block execution of other greenthreads. eventlet.sleep() # self._is_running being True indicates that the loop was # broken out of due to an error in the watched process rather # than the loop condition being satisfied. if self._is_running: self._is_running = False self._handle_process_error()
def _get_ip_link_output(cls): """Gets the output of the ip link help command Runs ip link help command and stores its output Note: ip link help return error and writes its output to stderr so we get the output from there. however, if this issue will be solved and the command will write to stdout, we will get the output from there too. """ try: ip_cmd = ['ip', 'link', 'help'] _stdout, _stderr = utils.execute( ip_cmd, check_exit_code=False, return_stderr=True, log_fail_as_error=False) except Exception as e: LOG.exception(_LE("Failed executing ip command")) raise UnsupportedIpLinkCommand(reason=e) return _stdout or _stderr
# * default_log_levels # # These variables default to respectively: # # import oslo_log # oslo_log._options.DEFAULT_LOG_LEVELS # oslo_log._options.log_opts[0].default # extra_log_level_defaults = ['dogpile=INFO', 'routes=INFO'] logging.set_defaults(default_log_levels=logging.get_default_log_levels() + extra_log_level_defaults) # Required setup based on configuration and domain logging.setup(CONF, DOMAIN) if __name__ == '__main__': prepare() # NOTE: These examples use Oslo i18n marker functions LOG.info(_LI("Welcome to Oslo Logging")) LOG.debug("A debugging message") # Debug messages are not translated LOG.warning(_LW("A warning occurred")) LOG.error(_LE("An error occurred")) try: raise Exception(_("This is exceptional")) except Exception: LOG.exception(_LE("An Exception occurred"))
def _apply_synchronized(self): """Apply the current in-memory set of iptables rules. This will create a diff between the rules from the previous runs and replace them with the current set of rules. This happens atomically, thanks to iptables-restore. Returns a list of the changes that were sent to iptables-save. """ s = [('iptables', self.ipv4)] if self.use_ipv6: s += [('ip6tables', self.ipv6)] all_commands = [] # variable to keep track all commands for return val for cmd, tables in s: args = ['%s-save' % (cmd, )] if self.namespace: args = ['ip', 'netns', 'exec', self.namespace] + args save_output = self.execute(args, run_as_root=True) all_lines = save_output.split('\n') commands = [] # Traverse tables in sorted order for predictable dump output for table_name in sorted(tables): table = tables[table_name] # isolate the lines of the table we are modifying start, end = self._find_table(all_lines, table_name) old_rules = all_lines[start:end] # generate the new table state we want new_rules = self._modify_rules(old_rules, table, table_name) # generate the iptables commands to get between the old state # and the new state changes = _generate_path_between_rules(old_rules, new_rules) if changes: # if there are changes to the table, we put on the header # and footer that iptables-save needs commands += (['# Generated by iptables_manager'] + ['*%s' % table_name] + changes + ['COMMIT', '# Completed by iptables_manager']) if not commands: continue all_commands += commands args = ['%s-restore' % (cmd, ), '-n'] if self.namespace: args = ['ip', 'netns', 'exec', self.namespace] + args try: # always end with a new line commands.append('') self.execute(args, process_input='\n'.join(commands), run_as_root=True) except RuntimeError as r_error: with excutils.save_and_reraise_exception(): try: line_no = int( re.search( 'iptables-restore: line ([0-9]+?) failed', str(r_error)).group(1)) context = IPTABLES_ERROR_LINES_OF_CONTEXT log_start = max(0, line_no - context) log_end = line_no + context except AttributeError: # line error wasn't found, print all lines instead log_start = 0 log_end = len(commands) log_lines = ('%7d. %s' % (idx, l) for idx, l in enumerate( commands[log_start:log_end], log_start + 1)) LOG.error( _LE("IPTablesManager.apply failed to apply the " "following set of iptables rules:\n%s"), '\n'.join(log_lines)) LOG.debug( "IPTablesManager.apply completed with success. %d iptables " "commands were issued", len(all_commands)) return all_commands
# # These variables default to respectively: # # import oslo_log # oslo_log._options.DEFAULT_LOG_LEVELS # oslo_log._options.log_opts[0].default # extra_log_level_defaults = [ 'dogpile=INFO', 'routes=INFO' ] logging.set_defaults( default_log_levels=logging.get_default_log_levels() + extra_log_level_defaults) # Required setup based on configuration and domain logging.setup(CONF, DOMAIN) if __name__ == '__main__': prepare() # NOTE: These examples use Oslo i18n marker functions LOG.info(_LI("Welcome to Oslo Logging")) LOG.debug("A debugging message") # Debug messages are not translated LOG.warning(_LW("A warning occured")) LOG.error(_LE("An error occured")) LOG.exception(_("An Exception occured"))
def _exit_action(self, service_id): LOG.error( _LE("Exiting agent as programmed in check_child_processes_" "actions")) self._exit_handler(service_id.uuid, service_id.service)
# import oslo_log # oslo_log._options.DEFAULT_LOG_LEVELS # oslo_log._options.log_opts[0].default # extra_log_level_defaults = [ 'dogpile=INFO', 'routes=INFO' ] logging.set_defaults( default_log_levels=logging.get_default_log_levels() + extra_log_level_defaults) # Required setup based on configuration and domain logging.setup(CONF, DOMAIN) if __name__ == '__main__': prepare() # NOTE: These examples use Oslo i18n marker functions LOG.info(_LI("Welcome to Oslo Logging")) LOG.debug("A debugging message") # Debug messages are not translated LOG.warning(_LW("A warning occurred")) LOG.error(_LE("An error occurred")) try: raise Exception(_("This is exceptional")) except Exception: LOG.exception(_LE("An Exception occurred"))
def _apply_synchronized(self): """Apply the current in-memory set of iptables rules. This will create a diff between the rules from the previous runs and replace them with the current set of rules. This happens atomically, thanks to iptables-restore. Returns a list of the changes that were sent to iptables-save. """ s = [('iptables', self.ipv4)] if self.use_ipv6: s += [('ip6tables', self.ipv6)] all_commands = [] # variable to keep track all commands for return val for cmd, tables in s: args = ['%s-save' % (cmd,)] if self.namespace: args = ['ip', 'netns', 'exec', self.namespace] + args save_output = self.execute(args, run_as_root=True) all_lines = save_output.split('\n') commands = [] # Traverse tables in sorted order for predictable dump output for table_name in sorted(tables): table = tables[table_name] # isolate the lines of the table we are modifying start, end = self._find_table(all_lines, table_name) old_rules = all_lines[start:end] # generate the new table state we want new_rules = self._modify_rules(old_rules, table, table_name) # generate the iptables commands to get between the old state # and the new state changes = _generate_path_between_rules(old_rules, new_rules) if changes: # if there are changes to the table, we put on the header # and footer that iptables-save needs commands += (['# Generated by iptables_manager'] + ['*%s' % table_name] + changes + ['COMMIT', '# Completed by iptables_manager']) if not commands: continue all_commands += commands args = ['%s-restore' % (cmd,), '-n'] if self.namespace: args = ['ip', 'netns', 'exec', self.namespace] + args try: # always end with a new line commands.append('') self.execute(args, process_input='\n'.join(commands), run_as_root=True) except RuntimeError as r_error: with excutils.save_and_reraise_exception(): try: line_no = int(re.search( 'iptables-restore: line ([0-9]+?) failed', str(r_error)).group(1)) context = IPTABLES_ERROR_LINES_OF_CONTEXT log_start = max(0, line_no - context) log_end = line_no + context except AttributeError: # line error wasn't found, print all lines instead log_start = 0 log_end = len(commands) log_lines = ('%7d. %s' % (idx, l) for idx, l in enumerate( commands[log_start:log_end], log_start + 1) ) LOG.error(_LE("IPTablesManager.apply failed to apply the " "following set of iptables rules:\n%s"), '\n'.join(log_lines)) LOG.debug("IPTablesManager.apply completed with success. %d iptables " "commands were issued", len(all_commands)) return all_commands
def _exit_action(self, service_id): LOG.error(_LE("Exiting agent as programmed in check_child_processes_" "actions")) self._exit_handler(service_id.uuid, service_id.service)
# # These variables default to respectively: # # import oslo_log # oslo_log._options.DEFAULT_LOG_LEVELS # oslo_log._options.log_opts[0].default # extra_log_level_defaults = [ 'dogpile=INFO', 'routes=INFO' ] logging.set_defaults( default_log_levels=logging.get_default_log_levels() + extra_log_level_defaults) # Required setup based on configuration and domain logging.setup(CONF, DOMAIN) if __name__ == '__main__': prepare() # NOTE: These examples use Oslo i18n marker functions LOG.info(_LI("Welcome to Oslo Logging")) LOG.debug("A debugging message") # Debug messages are not translated LOG.warning(_LW("A warning occurred")) LOG.error(_LE("An error occurred")) LOG.exception(_("An Exception occurred"))