Example #1
0
    def _SetConfig(self, destination_file, data, canary):
        # Canarying is not supported on BROCADE.
        if canary:
            raise exceptions.SetConfigCanaryingError(
                '%s devices do not support '
                'configuration canarying.' % self.vendor_name)
        # The result object.
        result = base_device.SetConfigResult()
        # Check for a connection to the Brocade.
        if not self._GetConnected():
            raise exceptions.SetConfigError('Cannot use unless already '
                                            'connected to the device.')

        if destination_file in self.NON_FILE_DESTINATIONS:
            # Use a random remote file name
            file_name = 'push.%s' % os.urandom(8).encode('hex')
        else:
            # Okay, the user is just copying a file, not a configuraiton into either
            # startup-config or running-config, therefore we should use the entire
            # path.
            file_name = destination_file

        # Copy the file to the router using SCP.
        scp = pexpect_connection.ScpPutConnection(host=self.loopback_ipv4,
                                                  username=self._username,
                                                  password=self._password)

        # This is a workaround. Brocade case: 537017.
        # Brocade changed all the filename to lowercases after scp
        file_name = file_name.lower()
        try:
            scp.Copy(data, destination_file='slot1:' + file_name)
        except pexpect_connection.Error, e:
            raise exceptions.SetConfigError(
                'Failed to copy configuration to remote device. %s' % str(e))
Example #2
0
    def _SetConfig(self, unused_destination_file, data, canary):
        """Upload config to a Brocade router (TI/FI).

    Args:
      unused_destination_file: Unused.
      data: A string, the data to copy to destination_file.
      canary: A boolean, if True, only canary check the configuration, don't
        apply it.

    Returns:
      A base_device.SetConfigResult.
      Transcript of any device interaction that occurred during the _SetConfig.

    Raises:
      exceptions.CmdError: An error occurred inside the call to _Cmd.
    """
        # Canarying is not supported on BROCADE.
        if canary:
            raise exceptions.SetConfigCanaryingError(
                '%s devices do not support '
                'configuration canarying.' % self.vendor_name)
        # The result object.
        result = base_device.SetConfigResult()
        # Check for a connection to the Brocade.
        if not self._GetConnected():
            raise exceptions.SetConfigError('Cannot use unless already '
                                            'connected to the device.')

        # Derive our config prompt from the discovered prompt.
        self._connection.config_prompt = re.compile(
            self._connection.re_prompt.pattern[:-2] + r'\(config\S*\)' +
            self._connection.re_prompt.pattern[-2:])

        # Enter config mode.
        self._connection.child.send('configure terminal\r')
        self._connection.child.expect('\r\n', timeout=self.timeout_response)
        self._connection.child.expect(self._connection.config_prompt,
                                      timeout=self.timeout_response,
                                      searchwindowsize=128)

        def SendAndWait(command):
            """Sends a command and waits for a response.

      Args:
        command: str; A single config line.

      Returns:
        A string; the last response.

      Raises:
        exceptions.SetConfigError: When we unexpectedly exit configuration mode
          while setting config.
      """
            self._connection.child.send(command + '\r')
            self._connection.child.expect('\r\n',
                                          timeout=self.timeout_response)
            pindex = self._connection.child.expect(
                [self._connection.config_prompt, self._connection.re_prompt],
                timeout=self.timeout_response,
                searchwindowsize=128)
            # We unexpectedly exited config mode. Too many exits or ctrl-z.
            if pindex == 1:
                raise exceptions.SetConfigError(
                    'Unexpectedly exited config mode after line: %s' % command)
            return self._connection.child.before.replace('\r\n', os.linesep)

        lines = [x.strip() for x in data.splitlines()]
        # Remove any 'end' lines. Multiple ends could be bad.
        lines = [line for line in lines if line != 'end']

        for line in lines:
            if next((line for prefix in self.verboten_config
                     if line.startswith(prefix)), False):
                raise exceptions.CmdError(
                    'Command %s is not permitted on Brocade devices.' % line)
            if line:
                line_result = SendAndWait(line)
                if (line_result.startswith('Invalid input -> ') or line_result
                        == 'Not authorized to execute this command.\n'):
                    raise exceptions.CmdError('Command failed: %s' %
                                              line_result)

        self._connection.child.send('end\r')
        self._connection.child.expect(self._connection.re_prompt,
                                      timeout=self.timeout_act_user)
        self._connection.child.send('wr mem\r')
        self._connection.child.expect(self._connection.re_prompt,
                                      timeout=self.timeout_act_user)
        self._Disconnect()
        result.transcript = 'SetConfig applied the file successfully.'
        return result
Example #3
0
  def _SetConfig(self, destination_file, data, canary):
    # Canarying is not supported on ASA.
    if canary:
      raise exceptions.SetConfigCanaryingError('%s devices do not support '
                                               'configuration canarying.' %
                                               self.vendor_name)
    # We only support copying to 'running-config' or 'startup-config' on ASA.
    if destination_file not in ('running-config', 'startup-config'):
      raise exceptions.SetConfigError('destination_file argument must be '
                                      '"running-config" or "startup-config" '
                                      'for %s devices.' % self.vendor_name)
    # Result object.
    result = base_device.SetConfigResult()

    # Get the MD5 sum of the file.
    local_digest = hashlib.md5(data).hexdigest()

    try:
      # Get the working path from the remote device
      remote_path = 'nvram:/'
    except exceptions.CmdError as e:
      msg = 'Error obtaining working directory: %s' % e
      logging.error(msg)
      raise exceptions.SetConfigError(msg)

    # Use a random remote file name
    remote_tmpfile = '%s/push.%s' % (
        remote_path.rstrip(), os.urandom(8).encode('hex'))

    # Upload the file to the device.
    scp = pexpect_connection.ScpPutConnection(
        self.loopback_ipv4,
        username=self._username,
        password=self._password)
    try:
      scp.Copy(data, remote_tmpfile)
    except pexpect_connection.Error as e:
      raise exceptions.SetConfigError(
          'Failed to copy configuration to remote device. %s' % str(e))

    # Get the file size on the router.
    try:
      # Get the MD5 hexdigest of the file on the remote device.
      try:
        verify_output = self._Cmd('verify /md5 %s' % remote_tmpfile)
        match = MD5_RE.search(verify_output)
        if match is not None:
          remote_digest = match.group(1)
        else:
          raise exceptions.SetConfigError(
              'The "verify /md5 <filename>" command did not produce '
              'expected results. It returned: %r' % verify_output)
      except exceptions.CmdError as e:
        raise exceptions.SetConfigError(
            'The MD5 hash command on the router did not succed. '
            'The device may not support: "verify /md5 <filename>"')
      # Verify the local_digest and remote_digest are the same.
      if local_digest != remote_digest:
        raise exceptions.SetConfigError(
            'File transfer to remote host corrupted. Local digest: %r, '
            'Remote digest: %r' % (local_digest, remote_digest))

      # Copy the file from flash to the
      # destination(running-config, startup-config).
      # Catch errors that may occur during application, and report
      # these to the user.
      try:
        self._connection.child.send(
            'copy %s %s\r' % (remote_tmpfile, destination_file))
        pindex = self._connection.child.expect(
            [r'Destination filename \[%s\]\?' % destination_file,
             r'%\s*\S*.*',
             r'%Error.*',
             self._connection.re_prompt],
            timeout=self.timeout_act_user)
        if pindex == 0:
          self._connection.child.send('\r')
          try:
            pindex = self._connection.child.expect(
                [r'Invalid input detected',
                 self._connection.re_prompt,
                 r'%Warning:There is a file already existing.*'
                 'Do you want to over write\? \[confirm\]'],
                timeout=self.timeout_act_user)
            if pindex == 0:
              # Search again using findall to get all bad lines.
              bad_lines = re.findall(
                  r'^(.*)$[\s\^]+% Invalid input',
                  self._connection.child.match.string,
                  re.MULTILINE)
              raise exceptions.SetConfigSyntaxError(
                  'Configuration loaded, but with bad lines:\n%s' %
                  '\n'.join(bad_lines))
            if pindex == 2:
              # Don't over-write.
              self._connection.child.send('n')
              raise exceptions.SetConfigError(
                  'Destination file %r already exists, cannot overwrite.'
                  % destination_file)
          except (pexpect.EOF, pexpect.TIMEOUT) as e:
            raise exceptions.SetConfigError(
                'Copied file to device, but did not '
                'receive prompt afterwards. %s %s' %
                (self._connection.child.before, self._connection.child.after))

        elif pindex == 2:
          print "MATCHED 2"
          # The expect does a re.search, search again using findall to get all
          raise exceptions.SetConfigError('Could not copy temporary '
                                          'file to %s.' % destination_file)
      except (pexpect.EOF, pexpect.TIMEOUT) as e:
        raise exceptions.SetConfigError(
            'Attempted to copy to bootflash, but a timeout occurred.')

      # We need to 'write memory' if we are doing running-config.
      if destination_file == 'running-config':
        logging.debug('Attempting to copy running-config to startup-config '
                     'on %s(%s)', self.host, self.loopback_ipv4)
        try:
          self._Cmd('wr mem')
        except exceptions.CmdError as e:
          raise exceptions.SetConfigError('Failed to write startup-config '
                                          'for %s(%s). Changes applied. '
                                          'Error was: %s' %
                                          (self.host, self.loopback_ipv4,
                                           str(e)))
    finally:
      try:
        self._DeleteFile(remote_tmpfile)
      except DeleteFileError as e:
        result.transcript = 'SetConfig warning: %s' % str(e)
        logging.warn(result.transcript)

    # And finally, return the result text.
    return result