def filter_command(command, rootwrap_config):
    # Load rootwrap configuration
    try:
        rawconfig = ConfigParser.RawConfigParser()
        rawconfig.read(rootwrap_config)
        rw_config = wrapper.RootwrapConfig(rawconfig)
    except ValueError as exc:
        LOG.error(_LE('Incorrect value in %(config)s: %(exc)s'), {
            'config': rootwrap_config,
            'exc': exc.message
        })
        sys.exit(errno.EINVAL)
    except ConfigParser.Error:
        LOG.error(_LE('Incorrect configuration file: %(config)s'),
                  {'config': rootwrap_config})
        sys.exit(errno.EINVAL)

    # Check if command matches any of the loaded filters
    filters = wrapper.load_filters(rw_config.filters_path)
    try:
        wrapper.match_filter(filters, command, exec_dirs=rw_config.exec_dirs)
    except wrapper.FilterMatchNotExecutable as exc:
        LOG.error(
            _LE('Command %(command)s is not executable: '
                '%(path)s (filter match = %(name)s)'), {
                    'command': command,
                    'path': exc.match.exec_path,
                    'name': exc.match.name
                })
        sys.exit(errno.EINVAL)
    except wrapper.NoFilterMatched:
        LOG.error(_LE('Unauthorized command: %(cmd)s (no filter matched)'),
                  {'cmd': command})
        sys.exit(errno.EPERM)
def filter_command(command, rootwrap_config):
    # Load rootwrap configuration
    try:
        rawconfig = ConfigParser.RawConfigParser()
        rawconfig.read(rootwrap_config)
        rw_config = wrapper.RootwrapConfig(rawconfig)
    except ValueError as exc:
        LOG.error(_LE('Incorrect value in %(config)s: %(exc)s'),
                  {'config': rootwrap_config, 'exc': exc.message})
        sys.exit(errno.EINVAL)
    except ConfigParser.Error:
        LOG.error(_LE('Incorrect configuration file: %(config)s'),
                  {'config': rootwrap_config})
        sys.exit(errno.EINVAL)

    # Check if command matches any of the loaded filters
    filters = wrapper.load_filters(rw_config.filters_path)
    try:
        wrapper.match_filter(filters, command, exec_dirs=rw_config.exec_dirs)
    except wrapper.FilterMatchNotExecutable as exc:
        LOG.error(_LE('Command %(command)s is not executable: '
                      '%(path)s (filter match = %(name)s)'),
                  {'command': command,
                   'path': exc.match.exec_path,
                   'name': exc.match.name})
        sys.exit(errno.EINVAL)
    except wrapper.NoFilterMatched:
        LOG.error(_LE('Unauthorized command: %(cmd)s (no filter matched)'),
                  {'cmd': command})
        sys.exit(errno.EPERM)
 def _request(self, method, url, **kwargs):
     """Perform REST request and save response info."""
     try:
         LOG.debug(
             "%(method)s: Request for %(resource)s payload: "
             "%(payload)s", {
                 'method': method.upper(),
                 'resource': url,
                 'payload': kwargs.get('data')
             })
         start_time = time.time()
         response = self.session.request(method,
                                         url,
                                         verify=False,
                                         timeout=self.timeout,
                                         **kwargs)
         LOG.debug("%(method)s Took %(time).2f seconds to process", {
             'method': method.upper(),
             'time': time.time() - start_time
         })
     except (r_exc.Timeout, r_exc.SSLError) as te:
         # Should never see SSLError, unless requests package is old (<2.0)
         timeout_val = 0.0 if self.timeout is None else self.timeout
         LOG.warning(
             _LW("%(method)s: Request timeout%(ssl)s "
                 "(%(timeout).3f sec) for CSR(%(host)s)"), {
                     'method': method,
                     'timeout': timeout_val,
                     'ssl':
                     '(SSLError)' if isinstance(te, r_exc.SSLError) else '',
                     'host': self.host
                 })
         self.status = requests.codes.REQUEST_TIMEOUT
     except r_exc.ConnectionError:
         LOG.exception(
             _LE("%(method)s: Unable to connect to "
                 "CSR(%(host)s)"), {
                     'method': method,
                     'host': self.host
                 })
         self.status = requests.codes.NOT_FOUND
     except Exception as e:
         LOG.error(
             _LE("%(method)s: Unexpected error for CSR (%(host)s): "
                 "%(error)s"), {
                     'method': method,
                     'host': self.host,
                     'error': e
                 })
         self.status = requests.codes.INTERNAL_SERVER_ERROR
     else:
         self.status = response.status_code
         LOG.debug("%(method)s: Completed [%(status)s]", {
             'method': method,
             'status': self.status
         })
         return self._response_info_for(response, method)
def execute_with_mount():
    conf = setup_conf()
    conf()
    config.setup_logging()
    if not conf.cmd:
        LOG.error(_LE('No command provided, exiting'))
        return errno.EINVAL

    if not conf.mount_paths:
        LOG.error(_LE('No mount path provided, exiting'))
        return errno.EINVAL

    # Both sudoers and rootwrap.conf will not exist in the directory /etc
    # after bind-mount, so we can't use utils.execute(conf.cmd,
    # run_as_root=True). That's why we have to check here if cmd matches
    # CommandFilter
    filter_command(conf.cmd, conf.rootwrap_config)

    # Make sure the process is running in net namespace invoked by ip
    # netns exec(/proc/[pid]/ns/net) which is since Linux 3.0,
    # as we can't check mount namespace(/proc/[pid]/ns/mnt)
    # which is since Linux 3.8. For more detail please refer the link
    # http://man7.org/linux/man-pages/man7/namespaces.7.html
    if os.path.samefile(os.path.join('/proc/1/ns/net'),
                        os.path.join('/proc', str(os.getpid()), 'ns/net')):
        LOG.error(_LE('Cannot run without netns, exiting'))
        return errno.EINVAL

    for path, new_path in conf.mount_paths.items():
        if not os.path.isdir(new_path):
            # Sometimes all directories are not ready
            LOG.debug('%s is not directory', new_path)
            continue
        if os.path.isdir(path) and os.path.isabs(path):
            return_code = execute(['mount', '--bind', new_path, path])
            if return_code == 0:
                LOG.info(
                    _LI('%(new_path)s has been '
                        'bind-mounted in %(path)s'), {
                            'new_path': new_path,
                            'path': path
                        })
            else:
                LOG.error(
                    _LE('Failed to bind-mount '
                        '%(new_path)s in %(path)s'), {
                            'new_path': new_path,
                            'path': path
                        })
    return execute(conf.cmd)
    def _do_request(self, method, resource, payload=None, more_headers=None,
                    full_url=False):
        """Perform a REST request to a CSR resource.

        If this is the first time interacting with the CSR, a token will
        be obtained. If the request fails, due to an expired token, the
        token will be obtained and the request will be retried once more.
        """

        if self.token is None:
            if not self.authenticate():
                return

        if full_url:
            url = resource
        else:
            url = ('https://%(host)s/api/v1/%(resource)s' %
                   {'host': self.host, 'resource': resource})
        headers = {'Accept': 'application/json', 'X-auth-token': self.token}
        if more_headers:
            headers.update(more_headers)
        if payload:
            payload = jsonutils.dumps(payload)
        response = self._request(method, url, data=payload, headers=headers)
        if self.status == requests.codes.UNAUTHORIZED:
            if not self.authenticate():
                return
            headers['X-auth-token'] = self.token
            response = self._request(method, url, data=payload,
                                     headers=headers)
        if self.status != requests.codes.REQUEST_TIMEOUT:
            return response
        LOG.error(_LE("%(method)s: Request timeout for CSR(%(host)s)"),
                  {'method': method, 'host': self.host})
    def authenticate(self):
        """Obtain a token to use for subsequent CSR REST requests.

        This is called when there is no token yet, or if the token has expired
        and attempts to use it resulted in an UNAUTHORIZED REST response.
        """

        url = URL_BASE % {'host': self.host, 'resource': 'auth/token-services'}
        headers = {'Content-Length': '0', 'Accept': 'application/json'}
        headers.update(HEADER_CONTENT_TYPE_JSON)
        LOG.debug(
            "%(auth)s with CSR %(host)s", {
                'auth':
                'Authenticating' if self.token is None else 'Reauthenticating',
                'host': self.host
            })
        self.token = None
        response = self._request("POST", url, headers=headers, auth=self.auth)
        if response:
            self.token = response['token-id']
            LOG.debug("Successfully authenticated with CSR %s", self.host)
            return True
        LOG.error(_LE("Failed authentication with CSR %(host)s [%(status)s]"),
                  {
                      'host': self.host,
                      'status': self.status
                  })
Exemple #7
0
 def disable(self):
     """Disabling the process."""
     try:
         if self.active:
             self.stop()
         self.remove_config()
     except RuntimeError:
         LOG.exception(_LE("Failed to disable vpn process on router %s"),
                       self.id)
Exemple #8
0
 def _resolve_fqdn(self, fqdn):
     # The first addrinfo member from the list returned by
     # socket.getaddrinfo is used for the address resolution.
     # The code doesn't filter for ipv4 or ipv6 address.
     try:
         addrinfo = socket.getaddrinfo(fqdn, None)[0]
         return addrinfo[-1][0]
     except socket.gaierror:
         LOG.exception(_LE("Peer address %s cannot be resolved"), fqdn)
Exemple #9
0
 def _resolve_fqdn(self, fqdn):
     # The first addrinfo member from the list returned by
     # socket.getaddrinfo is used for the address resolution.
     # The code doesn't filter for ipv4 or ipv6 address.
     try:
         addrinfo = socket.getaddrinfo(fqdn, None)[0]
         return addrinfo[-1][0]
     except socket.gaierror:
         LOG.exception(_LE("Peer address %s cannot be resolved"), fqdn)
 def set_admin_state(self, is_up):
     """Change the admin state for the IPSec connection."""
     self.csr.set_ipsec_connection_state(self.tunnel, admin_up=is_up)
     if self.csr.status != requests.codes.NO_CONTENT:
         state = "UP" if is_up else "DOWN"
         LOG.error(_LE("Unable to change %(tunnel)s admin state to "
                       "%(state)s"), {'tunnel': self.tunnel,
                                      'state': state})
         raise CsrAdminStateChangeFailure(tunnel=self.tunnel, state=state)
def execute_with_mount():
    conf = setup_conf()
    conf()
    config.setup_logging()
    if not conf.cmd:
        LOG.error(_LE('No command provided, exiting'))
        return errno.EINVAL

    if not conf.mount_paths:
        LOG.error(_LE('No mount path provided, exiting'))
        return errno.EINVAL

    # Both sudoers and rootwrap.conf will not exist in the directory /etc
    # after bind-mount, so we can't use utils.execute(conf.cmd,
    # run_as_root=True). That's why we have to check here if cmd matches
    # CommandFilter
    filter_command(conf.cmd, conf.rootwrap_config)

    # Make sure the process is running in net namespace invoked by ip
    # netns exec(/proc/[pid]/ns/net) which is since Linux 3.0,
    # as we can't check mount namespace(/proc/[pid]/ns/mnt)
    # which is since Linux 3.8. For more detail please refer the link
    # http://man7.org/linux/man-pages/man7/namespaces.7.html
    if os.path.samefile(os.path.join('/proc/1/ns/net'),
                        os.path.join('/proc', str(os.getpid()), 'ns/net')):
        LOG.error(_LE('Cannot run without netns, exiting'))
        return errno.EINVAL

    for path, new_path in six.iteritems(conf.mount_paths):
        if not os.path.isdir(new_path):
            # Sometimes all directories are not ready
            LOG.debug('%s is not directory', new_path)
            continue
        if os.path.isdir(path) and os.path.isabs(path):
            return_code = execute(['mount', '--bind', new_path, path])
            if return_code == 0:
                LOG.info(_LI('%(new_path)s has been '
                         'bind-mounted in %(path)s'),
                         {'new_path': new_path, 'path': path})
            else:
                LOG.error(_LE('Failed to bind-mount '
                          '%(new_path)s in %(path)s'),
                          {'new_path': new_path, 'path': path})
    return execute(conf.cmd)
Exemple #12
0
 def disable(self):
     """Disabling the process."""
     try:
         if self.active:
             self.stop()
         self.remove_config()
     except RuntimeError:
         LOG.exception(
             _LE("Failed to disable vpn process on router %s"),
             self.id)
Exemple #13
0
 def enable(self):
     """Enabling the process."""
     try:
         self.ensure_configs()
         if self.active:
             self.restart()
         else:
             self.start()
     except RuntimeError:
         LOG.exception(_LE("Failed to enable vpn process on router %s"),
                       self.id)
Exemple #14
0
 def do_create_action(self, action_suffix, info, resource_id, title):
     """Perform a single REST step for IPSec site connection create."""
     create_action = 'create_%s' % action_suffix
     try:
         getattr(self.csr, create_action)(info)
     except AttributeError:
         LOG.exception(_LE("Internal error - '%s' is not defined"),
                       create_action)
         raise CsrResourceCreateFailure(resource=title, which=resource_id)
     self._check_create(title, resource_id)
     self.steps.append(RollbackStep(action_suffix, resource_id, title))
 def _check_create(self, resource, which):
     """Determine if REST create request was successful."""
     if self.csr.status == requests.codes.CREATED:
         LOG.debug("%(resource)s %(which)s is configured",
                   {'resource': resource, 'which': which})
         return
     LOG.error(_LE("Unable to create %(resource)s %(which)s: "
                   "%(status)d"),
               {'resource': resource, 'which': which,
                'status': self.csr.status})
     # ToDO(pcm): Set state to error
     raise CsrResourceCreateFailure(resource=resource, which=which)
Exemple #16
0
 def set_admin_state(self, is_up):
     """Change the admin state for the IPSec connection."""
     self.csr.set_ipsec_connection_state(self.tunnel, admin_up=is_up)
     if self.csr.status != requests.codes.NO_CONTENT:
         state = "UP" if is_up else "DOWN"
         LOG.error(
             _LE("Unable to change %(tunnel)s admin state to "
                 "%(state)s"), {
                     'tunnel': self.tunnel,
                     'state': state
                 })
         raise CsrAdminStateChangeFailure(tunnel=self.tunnel, state=state)
 def do_create_action(self, action_suffix, info, resource_id, title):
     """Perform a single REST step for IPSec site connection create."""
     create_action = 'create_%s' % action_suffix
     try:
         getattr(self.csr, create_action)(info)
     except AttributeError:
         LOG.exception(_LE("Internal error - '%s' is not defined"),
                       create_action)
         raise CsrResourceCreateFailure(resource=title,
                                        which=resource_id)
     self._check_create(title, resource_id)
     self.steps.append(RollbackStep(action_suffix, resource_id, title))
Exemple #18
0
 def enable(self):
     """Enabling the process."""
     try:
         self.ensure_configs()
         if self.active:
             self.restart()
         else:
             self.start()
     except RuntimeError:
         LOG.exception(
             _LE("Failed to enable vpn process on router %s"),
             self.id)
 def _request(self, method, url, **kwargs):
     """Perform REST request and save response info."""
     try:
         LOG.debug("%(method)s: Request for %(resource)s payload: "
                   "%(payload)s",
                   {'method': method.upper(), 'resource': url,
                    'payload': kwargs.get('data')})
         start_time = time.time()
         response = self.session.request(method, url, verify=False,
                                         timeout=self.timeout, **kwargs)
         LOG.debug("%(method)s Took %(time).2f seconds to process",
                   {'method': method.upper(),
                    'time': time.time() - start_time})
     except (r_exc.Timeout, r_exc.SSLError) as te:
         # Should never see SSLError, unless requests package is old (<2.0)
         timeout_val = 0.0 if self.timeout is None else self.timeout
         LOG.warning(_LW("%(method)s: Request timeout%(ssl)s "
                         "(%(timeout).3f sec) for CSR(%(host)s)"),
                     {'method': method,
                      'timeout': timeout_val,
                      'ssl': '(SSLError)'
                      if isinstance(te, r_exc.SSLError) else '',
                      'host': self.host})
         self.status = requests.codes.REQUEST_TIMEOUT
     except r_exc.ConnectionError:
         LOG.exception(_LE("%(method)s: Unable to connect to "
                           "CSR(%(host)s)"),
                       {'method': method, 'host': self.host})
         self.status = requests.codes.NOT_FOUND
     except Exception as e:
         LOG.error(_LE("%(method)s: Unexpected error for CSR (%(host)s): "
                       "%(error)s"),
                   {'method': method, 'host': self.host, 'error': e})
         self.status = requests.codes.INTERNAL_SERVER_ERROR
     else:
         self.status = response.status_code
         LOG.debug("%(method)s: Completed [%(status)s]",
                   {'method': method, 'status': self.status})
         return self._response_info_for(response, method)
 def do_rollback(self):
     """Undo create steps that were completed successfully."""
     for step in reversed(self.steps):
         delete_action = 'delete_%s' % step.action
         LOG.debug("Performing rollback action %(action)s for "
                   "resource %(resource)s", {'action': delete_action,
                                             'resource': step.title})
         try:
             getattr(self.csr, delete_action)(step.resource_id)
         except AttributeError:
             LOG.exception(_LE("Internal error - '%s' is not defined"),
                           delete_action)
             raise CsrResourceCreateFailure(resource=step.title,
                                            which=step.resource_id)
         self._verify_deleted(self.csr.status, step.title, step.resource_id)
     self.steps = []
Exemple #21
0
 def _check_create(self, resource, which):
     """Determine if REST create request was successful."""
     if self.csr.status == requests.codes.CREATED:
         LOG.debug("%(resource)s %(which)s is configured", {
             'resource': resource,
             'which': which
         })
         return
     LOG.error(
         _LE("Unable to create %(resource)s %(which)s: "
             "%(status)d"), {
                 'resource': resource,
                 'which': which,
                 'status': self.csr.status
             })
     # ToDO(pcm): Set state to error
     raise CsrResourceCreateFailure(resource=resource, which=which)
Exemple #22
0
    def _cleanup_control_files(self):
        try:
            ctl_file = '%s.ctl' % self.pid_path
            LOG.debug('Removing %(pidfile)s and %(ctlfile)s',
                      {'pidfile': self.pid_file,
                       'ctlfile': ctl_file})

            if os.path.exists(self.pid_file):
                os.remove(self.pid_file)

            if os.path.exists(ctl_file):
                os.remove(ctl_file)

        except OSError as e:
            LOG.error(_LE('Unable to remove pluto control '
                          'files for router %(router)s. %(msg)s'),
                      {'router': self.id, 'msg': e})
Exemple #23
0
    def _cleanup_control_files(self):
        try:
            ctl_file = '%s.ctl' % self.pid_path
            LOG.debug('Removing %(pidfile)s and %(ctlfile)s',
                      {'pidfile': self.pid_file,
                       'ctlfile': ctl_file})

            if os.path.exists(self.pid_file):
                os.remove(self.pid_file)

            if os.path.exists(ctl_file):
                os.remove(ctl_file)

        except OSError as e:
            LOG.error(_LE('Unable to remove pluto control '
                          'files for router %(router)s. %(msg)s'),
                      {'router': self.id, 'msg': e})
    def get_vpn_services_on_host(self, context, host):
        # make RPC call to neutron server
        cctxt = self.client.prepare()
        data = cctxt.call(context, 'get_vpn_services_on_host', host=host)

        vpn_services = list()
        for svc in data:
            try:
                for conn in svc[_KEY_CONNECTIONS]:
                    vyatta_vpn_config.validate_svc_connection(conn)
            except v_exc.InvalidVPNServiceError:
                LOG.error(_LE('Invalid or incomplete VPN service data: '
                              'id={id}').format(id=svc.get('id')))
                continue
            vpn_services.append(svc)

        # return transformed data to caller
        return vpn_services
    def _do_request(self,
                    method,
                    resource,
                    payload=None,
                    more_headers=None,
                    full_url=False):
        """Perform a REST request to a CSR resource.

        If this is the first time interacting with the CSR, a token will
        be obtained. If the request fails, due to an expired token, the
        token will be obtained and the request will be retried once more.
        """

        if self.token is None:
            if not self.authenticate():
                return

        if full_url:
            url = resource
        else:
            url = ('https://%(host)s/api/v1/%(resource)s' % {
                'host': self.host,
                'resource': resource
            })
        headers = {'Accept': 'application/json', 'X-auth-token': self.token}
        if more_headers:
            headers.update(more_headers)
        if payload:
            payload = jsonutils.dumps(payload)
        response = self._request(method, url, data=payload, headers=headers)
        if self.status == requests.codes.UNAUTHORIZED:
            if not self.authenticate():
                return
            headers['X-auth-token'] = self.token
            response = self._request(method,
                                     url,
                                     data=payload,
                                     headers=headers)
        if self.status != requests.codes.REQUEST_TIMEOUT:
            return response
        LOG.error(_LE("%(method)s: Request timeout for CSR(%(host)s)"), {
            'method': method,
            'host': self.host
        })
Exemple #26
0
 def do_rollback(self):
     """Undo create steps that were completed successfully."""
     for step in reversed(self.steps):
         delete_action = 'delete_%s' % step.action
         LOG.debug(
             "Performing rollback action %(action)s for "
             "resource %(resource)s", {
                 'action': delete_action,
                 'resource': step.title
             })
         try:
             getattr(self.csr, delete_action)(step.resource_id)
         except AttributeError:
             LOG.exception(_LE("Internal error - '%s' is not defined"),
                           delete_action)
             raise CsrResourceCreateFailure(resource=step.title,
                                            which=step.resource_id)
         self._verify_deleted(self.csr.status, step.title, step.resource_id)
     self.steps = []
Exemple #27
0
    def _process_running(self):
        """Checks if process is still running."""

        # If no PID file, we assume the process is not running.
        if not os.path.exists(self.pid_file):
            return False

        try:
            # We take an ask-forgiveness-not-permission approach and rely
            # on throwing to tell us something. If the pid file exists,
            # delve into the process information and check if it matches
            # our expected command line.
            with open(self.pid_file, 'r') as f:
                pid = f.readline().strip()
                with open('/proc/%s/cmdline' % pid) as cmd_line_file:
                    cmd_line = cmd_line_file.readline()
                    if self.pid_path in cmd_line and 'pluto' in cmd_line:
                        # Okay the process is probably a pluto process
                        # and it contains the pid_path in the command
                        # line... could be a race. Log to error and return
                        # that it is *NOT* okay to clean up files. We are
                        # logging to error instead of debug because it
                        # indicates something bad has happened and this is
                        # valuable information for figuring it out.
                        LOG.error(
                            _LE('Process %(pid)s exists with command '
                                'line %(cmd_line)s.') % {
                                    'pid': pid,
                                    'cmd_line': cmd_line
                                })
                        return True

        except IOError as e:
            # This is logged as "info" instead of error because it simply
            # means that we couldn't find the files to check on them.
            LOG.info(
                _LI('Unable to find control files on startup for '
                    'router %(router)s: %(msg)s'), {
                        'router': self.id,
                        'msg': e
                    })
        return False
    def authenticate(self):
        """Obtain a token to use for subsequent CSR REST requests.

        This is called when there is no token yet, or if the token has expired
        and attempts to use it resulted in an UNAUTHORIZED REST response.
        """

        url = URL_BASE % {'host': self.host, 'resource': 'auth/token-services'}
        headers = {'Content-Length': '0',
                   'Accept': 'application/json'}
        headers.update(HEADER_CONTENT_TYPE_JSON)
        LOG.debug("%(auth)s with CSR %(host)s",
                  {'auth': 'Authenticating' if self.token is None
                   else 'Reauthenticating', 'host': self.host})
        self.token = None
        response = self._request("POST", url, headers=headers, auth=self.auth)
        if response:
            self.token = response['token-id']
            LOG.debug("Successfully authenticated with CSR %s", self.host)
            return True
        LOG.error(_LE("Failed authentication with CSR %(host)s [%(status)s]"),
                  {'host': self.host, 'status': self.status})
Exemple #29
0
    def _process_running(self):
        """Checks if process is still running."""

        # If no PID file, we assume the process is not running.
        if not os.path.exists(self.pid_file):
            return False

        try:
            # We take an ask-forgiveness-not-permission approach and rely
            # on throwing to tell us something. If the pid file exists,
            # delve into the process information and check if it matches
            # our expected command line.
            with open(self.pid_file, 'r') as f:
                pid = f.readline().strip()
                with open('/proc/%s/cmdline' % pid) as cmd_line_file:
                    cmd_line = cmd_line_file.readline()
                    if self.pid_path in cmd_line and 'pluto' in cmd_line:
                        # Okay the process is probably a pluto process
                        # and it contains the pid_path in the command
                        # line... could be a race. Log to error and return
                        # that it is *NOT* okay to clean up files. We are
                        # logging to error instead of debug because it
                        # indicates something bad has happened and this is
                        # valuable information for figuring it out.
                        LOG.error(_LE('Process %(pid)s exists with command '
                                  'line %(cmd_line)s.') %
                                  {'pid': pid, 'cmd_line': cmd_line})
                        return True

        except IOError as e:
            # This is logged as "info" instead of error because it simply
            # means that we couldn't find the files to check on them.
            LOG.info(_LI('Unable to find control files on startup for '
                         'router %(router)s: %(msg)s'),
                     {'router': self.id, 'msg': e})
        return False