-
Notifications
You must be signed in to change notification settings - Fork 1k
[advanced reboot] Add Paramiko module for device connection #1542
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 1 commit
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,66 @@ | ||
| import paramiko | ||
| import logging | ||
| from paramiko.ssh_exception import BadHostKeyException, AuthenticationException, SSHException | ||
|
|
||
| logger = logging.getLogger(__name__) | ||
|
|
||
| DEFAULT_CMD_EXECUTION_TIMEOUT_SEC = 10 | ||
|
|
||
| class DeviceConnection: | ||
| ''' | ||
| DeviceConnection uses Pramiko module to connect to devices | ||
|
|
||
| Pramiko module uses fallback mechanism where it would first try to use | ||
| ssh key and that fails, it will attempt username/password combination | ||
| ''' | ||
| def __init__(self, hostname, username, password=None): | ||
| ''' | ||
| Class constructor | ||
|
|
||
| @param hostname: hostname of device to connect to | ||
| @param username: username for device connection | ||
| @param password: password for device connection | ||
| ''' | ||
| self.hostname = hostname | ||
| self.username = username | ||
| self.password = password | ||
|
|
||
| def execCommand(self, cmd, timeout=DEFAULT_CMD_EXECUTION_TIMEOUT_SEC): | ||
| ''' | ||
| Executes command on remote device | ||
|
|
||
| @param cmd: command to be run on remote device | ||
| @param timeout: timeout for command run session | ||
| @return: stdout, stderr, value | ||
| stdout is a list of lines of the remote stdout gathered during command execution | ||
| stderr is a list of lines of the remote stderr gathered during command execution | ||
| value: 0 if command execution raised no exception | ||
| nonzero if exception is raised | ||
| ''' | ||
| client = paramiko.SSHClient() | ||
| client.set_missing_host_key_policy(paramiko.AutoAddPolicy()) | ||
|
|
||
| if isinstance(cmd, list): | ||
| cmd = ' '.join(cmd) | ||
|
|
||
| stdOut = stdErr = [] | ||
| retValue = 1 | ||
| try: | ||
| client.connect(self.hostname, username=self.username, password=self.password, allow_agent=False) | ||
| si, so, se = client.exec_command(cmd, timeout=timeout) | ||
| stdOut = so.readlines() | ||
| stdErr = se.readlines() | ||
| retValue = 0 | ||
| except SSHException as sshException: | ||
| logger.error('SSH Command failed with message: %s' % sshException) | ||
| pass | ||
| except AuthenticationException as authenticationException: | ||
| logger.error('SSH Authentiaction failure with message: %s' % authenticationException) | ||
| pass | ||
| except BadHostKeyException as badHostKeyException: | ||
| logger.error('SSH Authentiaction failure with message: %s' % badHostKeyException) | ||
| pass | ||
| finally: | ||
| client.close() | ||
|
|
||
| return stdOut, stdErr, retValue | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -115,9 +115,14 @@ def __buildTestbedData(self): | |
|
|
||
| self.rebootData['dut_hostname'] = self.mgFacts['minigraph_mgmt_interface']['addr'] | ||
| self.rebootData['dut_mac'] = hostFacts['ansible_Ethernet0']['macaddress'] | ||
| self.rebootData['dut_username'] = hostFacts['ansible_env']['SUDO_USER'] | ||
| self.rebootData['vlan_ip_range'] = self.mgFacts['minigraph_vlan_interfaces'][0]['subnet'] | ||
| self.rebootData['dut_vlan_ip'] = self.mgFacts['minigraph_vlan_interfaces'][0]['addr'] | ||
|
|
||
| invetory = self.duthost.host.options['inventory'].split('/')[-1] | ||
| secrets = self.duthost.host.options['variable_manager']._hostvars[self.duthost.hostname]['secret_group_vars'] | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I faced In the next two lines, you tried to get The values are defined in I, Maybe, missed something in testbed definition, but do we really need to get If No, then where secret_group_vars should be defined? @tahmed-dev Could you please help me?
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @msosyak Thanks for your question. Those secrets are defined in secrets.json file (ansible/group_vars/all/secrets.json). Here is the format of the file:
|
||
| self.rebootData['dut_username'] = secrets[invetory]['sonicadmin_user'] | ||
| self.rebootData['dut_password'] = secrets[invetory]['sonicadmin_password'] | ||
|
|
||
| self.rebootData['default_ip_range'] = str( | ||
| ipaddress.ip_interface(self.mgFacts['minigraph_vlan_interfaces'][0]['addr'] + '/16').network | ||
| ) | ||
|
|
@@ -223,13 +228,24 @@ def __prepareTestbedSshKeys(self, dutUsername, dutIp): | |
| self.ptfhost.shell('ssh-keygen -f /root/.ssh/known_hosts -R ' + dutIp) | ||
|
|
||
| logger.info('Generate public key for ptf host') | ||
| self.ptfhost.shell('ssh-keygen -b 2048 -t rsa -f /root/.ssh/id_rsa -q -N ""') | ||
| result = self.ptfhost.shell('cat /root/.ssh/id_rsa.pub') | ||
| self.ptfhost.file(path='/root/.ssh/', mode='u+rwx,g-rwx,o-rwx', state='directory') | ||
| result = self.ptfhost.openssh_keypair( | ||
| path='/root/.ssh/id_rsa', | ||
| size=2048, | ||
| force=True, | ||
| type='rsa', | ||
| mode='u=rw,g=,o=' | ||
| ) | ||
| # There is an error with id_rsa.pub access permissions documented in: | ||
| # https://github.com/ansible/ansible/issues/61411 | ||
| # @TODO: remove the following line when upgrading to Ansible 2.9x | ||
| self.ptfhost.file(path='/root/.ssh/id_rsa.pub', mode='u=rw,g=,o=') | ||
|
|
||
| cmd = ''' | ||
| mkdir -p /home/{0}/.ssh && | ||
| echo "{1}" >> /home/{0}/.ssh/authorized_keys && | ||
| chown -R {0}:{0} /home/{0}/.ssh/ | ||
| '''.format(dutUsername, result['stdout']) | ||
| '''.format(dutUsername, result['public_key']) | ||
| self.duthost.shell(cmd) | ||
|
|
||
| def __handleMellanoxDut(self): | ||
|
|
@@ -423,6 +439,7 @@ def __runPtfRunner(self, rebootOper=None): | |
| platform="remote", | ||
| params={ | ||
| "dut_username" : self.rebootData['dut_username'], | ||
| "dut_password" : self.rebootData['dut_password'], | ||
| "dut_hostname" : self.rebootData['dut_hostname'], | ||
| "reboot_limit_in_seconds" : self.rebootLimit, | ||
| "reboot_type" :self.rebootType, | ||
|
|
@@ -452,15 +469,17 @@ def __restorePrevImage(self): | |
| ''' | ||
| Resotre previous image and reboot DUT | ||
| ''' | ||
| logger.info('Restore current image') | ||
| self.duthost.shell('sonic_installer set_default {0}'.format(self.currentImage)) | ||
|
|
||
| rebootDut( | ||
| self.duthost, | ||
| self.localhost, | ||
| reboot_type=self.rebootType.replace('-reboot', ''), | ||
| wait = 180 + self.readyTimeout | ||
| ) | ||
| currentImage = self.duthost.shell('sonic_installer list | grep Current | cut -f2 -d " "')['stdout'] | ||
| if currentImage != self.currentImage: | ||
| logger.info('Restore current image') | ||
| self.duthost.shell('sonic_installer set_default {0}'.format(self.currentImage)) | ||
|
|
||
| rebootDut( | ||
| self.duthost, | ||
| self.localhost, | ||
| reboot_type=self.rebootType.replace('-reboot', ''), | ||
| wait = self.readyTimeout | ||
| ) | ||
|
|
||
| def tearDown(self): | ||
| ''' | ||
|
|
||
Uh oh!
There was an error while loading. Please reload this page.