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))
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
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