Пример #1
0
    def test_show_images(self, mock_expectmore, input_file, test_file):
        """Expect this to try to list the current and available firmware images."""
        test_switch = SwitchMellanoxM7800(switch_ip_address='fakeip',
                                          password='******')
        test_console_response = Path(
            test_file(input_file)).read_text().splitlines()

        expected_data = (
            {
                1: 'X86_64 3.6.4006 2017-07-03 16:17:39 x86_64',
                2: 'X86_64 3.6.4006 2017-07-03 16:17:39 x86_64',
            },
            2,
            1,
            [
                ('image-X86_64-3.6.2002.img',
                 'X86_64 3.6.2002 2016-09-28 21:00:15 x86_64'),
                ('image-X86_64-3.6.3004.img',
                 'X86_64 3.6.3004 2017-02-05 17:31:53 x86_64'),
                ('image-X86_64-3.6.4006.img',
                 'X86_64 3.6.4006 2017-07-03 16:17:39 x86_64'),
                ('image-X86_64-3.6.5009.img',
                 'X86_64 3.6.5009 2018-01-02 07:42:21 x86_64'),
                ('image-X86_64-3.6.8010.img',
                 'X86_64 3.6.8010 2018-08-20 18:04:19 x86_64'),
            ],
        )
        mock_expectmore.return_value.ask.return_value = test_console_response
        assert (test_switch.show_images() == expected_data)
        mock_expectmore.return_value.ask.assert_called_with('show images')
Пример #2
0
 def test_show_images_no_data(self, mock_expectmore):
     """Test that an error is raised when nothing is returned from the switch."""
     mock_expectmore.return_value.ask.return_value = []
     test_switch = SwitchMellanoxM7800(switch_ip_address='fakeip',
                                       password='******')
     with pytest.raises(SwitchException):
         test_switch.show_images()
Пример #3
0
	def test_write_configuration_errors(self, mock__get_errors, mock_expectmore):
		"""Expect that a fallback reboot command raises an exception on errors."""
		mock__get_errors.return_value = ['a returned error message']

		test_switch = SwitchMellanoxM7800(switch_ip_address = 'fakeip', password = '******')
		with pytest.raises(SwitchException):
			test_switch.write_configuration()
Пример #4
0
	def test_connect_password_auth_factory_fallback(self, mock_expectmore):
		"""Expect connect to authenticate using the default password and set up for factory default
		login if the user specified one doesn't work.
		"""
		mock_expectmore.return_value.isalive.return_value = False
		# perform password auth
		mock_expectmore.return_value.match_index = 0
		# set the first say to raise ExpectMoreException and the second to work
		mock_expectmore.return_value.say.side_effect = (
			ExpectMoreException,
			DEFAULT,
		)
		test_switch = SwitchMellanoxM7800(switch_ip_address = 'fakeip', password = '******')
		test_switch.connect()
		# expect the default password to be used
		mock_expectmore.return_value.say.assert_called_with('admin')
		# expect the extra factory default step to be prepended to the login sequence
		mock_expectmore.return_value.conversation.assert_called_with(
			[
				('', 'no'),
				([' >', ''], 'terminal length 999'),
				(' >', 'enable'),
				(' #', 'configure terminal'),
				('.config. #', ''),
			]
		)
Пример #5
0
	def test__get_boot_partitions(self, mock_expectmore, input_file, test_file):
		"""Test that partitions are found when present."""
		test_console_response = Path(test_file(input_file)).read_text().splitlines()

		expected_data = (2, 1)
		test_switch = SwitchMellanoxM7800(switch_ip_address = 'fakeip', password = '******')
		assert(test_switch._get_boot_partitions(command_response = test_console_response) == expected_data)
Пример #6
0
	def test_install_firmware_not_enough_steps(self, mock_expectmore):
		"""Expect an error because there were not enough steps that appeared to be performed."""
		test_switch = SwitchMellanoxM7800(switch_ip_address = 'fakeip', password = '******')
		firmware_name = 'my_amazing_firmware'
		mock_expectmore.return_value.ask.return_value = ['Step 1 of 4: Verify Image', '100.0%', '100.0%', '100.0%',]
		with pytest.raises(SwitchException):
			test_switch.install_firmware(image = firmware_name)
Пример #7
0
	def test_image_boot_next_error(self, mock_expectmore):
		"""Expect this to raise an error if there is an error response."""
		test_switch = SwitchMellanoxM7800(switch_ip_address = 'fakeip', password = '******')
		error_message = '% strange error'
		mock_expectmore.return_value.ask.return_value = [error_message]
		with pytest.raises(SwitchException, match=error_message) as exception:
			test_switch.image_boot_next()
Пример #8
0
	def test_connect(self, mock_mellanoknok, mock_expectmore):
		"""Test that connect runs as expected in the simple case."""
		mock_expectmore.return_value.isalive.return_value = False
		# don't perform password auth
		mock_expectmore.return_value.match_index = -1
		test_switch = SwitchMellanoxM7800(switch_ip_address = 'fakeip', password = '******')
		test_switch.connect()
		# expect isalive to be checked
		mock_expectmore.return_value.isalive.assert_called()
		# expect the connection to be started
		mock_expectmore.return_value.start.assert_called_with(
			f'ssh -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null -tt {test_switch.username}@{test_switch.switch_ip_address}'
		)
		# expect it to look for the prompts before performing the login sequence
		mock_expectmore.return_value.wait.assert_called_with(['Password:'******' >'])
		# expect it to run the login sequence
		mock_expectmore.return_value.conversation.assert_called_with(
			[
				([' >', ''], 'terminal length 999'),
				(' >', 'enable'),
				(' #', 'configure terminal'),
				('.config. #', ''),
			]
		)
		# expect it to try to establish an API connection
		mock_mellanoknok.assert_called_with(test_switch.switch_ip_address, password = test_switch.password)
Пример #9
0
	def test__get_boot_partitions_out_of_order(self, mock_expectmore):
		"""Test that partitions numbers are returned in the correct order (last, next)."""
		test_console_response = ['Next boot partition: 2', 'Last boot partition: 1',]

		expected_data = (1, 2)
		test_switch = SwitchMellanoxM7800(switch_ip_address = 'fakeip', password = '******')
		assert(test_switch._get_boot_partitions(command_response = test_console_response) == expected_data)
Пример #10
0
	def test__get_expected_errors_no_error_pattern_found(self, mock_expectmore):
		"""Confirm that a string is returned when no error patterns are found"""
		test_response = ['foobar%%%%%%', 'f%o%b%a%r%', 'foobar%', 'errors!']
		test_switch = SwitchMellanoxM7800(switch_ip_address = 'fakeip', password = '******')
		errors = test_switch._get_expected_errors(command_response = test_response)
		assert(isinstance(errors, str))
		assert(errors)
Пример #11
0
 def test__get_installed_images_errors(self, mock_expectmore,
                                       test_response):
     """Test that a SwitchException is raised when invalid responses are parsed."""
     test_switch = SwitchMellanoxM7800(switch_ip_address='fakeip',
                                       password='******')
     with pytest.raises(SwitchException):
         test_switch._get_installed_images(command_response=test_response)
Пример #12
0
	def update_firmware(self, switch_name, firmware_url, downgrade, **kwargs):
		try:
			# connect to the switch and run the firmware upgrade procedure
			m7800_switch = SwitchMellanoxM7800(switch_name, **kwargs)
			m7800_switch.connect()
			# delete all stored images on the switch before sending ours over
			for image in m7800_switch.show_images().images_fetched_and_available:
				m7800_switch.image_delete(image = image.filename)

			m7800_switch.image_fetch(url = firmware_url)
			# install the firmware we just sent to the switch
			m7800_switch.install_firmware(
				# grab the filename from the switch on purpose in case it does something funky with it
				image = m7800_switch.show_images().images_fetched_and_available[0].filename
			)
			# set the switch to boot from our installed image
			m7800_switch.image_boot_next()
			# perform extra downgrade steps if necessary
			if downgrade:
				# need to force a boot, even if the old code parsing the new configuration fails.
				m7800_switch.disable_fallback_reboot()
				m7800_switch.write_configuration()
				m7800_switch.reload()
				# now wait for the switch to come back.
				reconnected = False
				# timeout after 30 minutes. We use a no-op lambda because we just want to know when the timer expired.
				timer = Timer(1800, lambda: ())
				timer.start()
				while timer.is_alive():
					# swallow the expected exceptions while trying to connect to a switch that isn't ready yet.
					with suppress(SwitchException, ExpectMoreException):
						# use the switch as a context manager so every time the connect or factory reset fails,
						# we disconnect from the switch.
						with m7800_switch:
							m7800_switch.connect()
							# now factory reset the switch, which will reboot it again.
							# The successful connect above doesn't seem to guarantee that we can fire a
							# factory reset command, so we try in this loop.
							m7800_switch.factory_reset()
							timer.cancel()
							reconnected = True

				if not reconnected:
					raise CommandError(
						cmd = self.owner,
						msg = f'Unable to reconnect {switch_name} to switch while performing downgrade procedure.'
					)
			else:
				m7800_switch.reload()
		# Turn some potentially verbose and detailed error messages into something more end user friendly
		# while keeping the dirty details available in the logs.
		except (SwitchException, ExpectMoreException) as exception:
			stack.commands.Log(
				message = f'Error during firmware update on {switch_name}: {exception}',
				level = syslog.LOG_ERR
			)
			raise CommandError(
				cmd = self.owner,
				msg = f'Failed to update firmware on {switch_name}.'
			)
Пример #13
0
	def run(self, hosts):
		if not self.owner.expanded:
			return {'keys': [], 'values': {}}

		switch_attrs = self.owner.getHostAttrDict('a:switch')

		host_info = dict.fromkeys(hosts)
		for host in dict(host_info):
			make, model = (switch_attrs[host].get('component.make'), switch_attrs[host].get('component.model'))
			if (make, model) != ('Mellanox', 'm7800'):
				# ... but set other hosts to an empty value instead of False
				host_info[host] = (None, None)
				continue

			kwargs = {
				'username': switch_attrs[host].get('switch_username'),
				'password': switch_attrs[host].get('switch_password'),
			}

			# remove username and pass attrs (aka use any pylib defaults) if they aren't host attrs
			kwargs = {k:v for k, v in kwargs.items() if v is not None}

			s = SwitchMellanoxM7800(host, **kwargs)
			try:
				s.connect()
			except SwitchException as e:
				host_info[host] = (None, switch_attrs[host].get('ibfabric', None))
				continue

			host_info[host] = (s.subnet_manager, switch_attrs[host].get('ibfabric', None))

		return { 'keys' : ['ib subnet manager', 'ib fabric'],
			'values': host_info }
Пример #14
0
 def test__get_expected_errors(self, mock_expectmore):
     """Confirm that strings starting with '%' are treated as errors."""
     test_response = ['foobar%%%%%%', 'f%o%b%a%r%', '%foobar%', '%errors!']
     test_switch = SwitchMellanoxM7800(switch_ip_address='fakeip',
                                       password='******')
     expected = sorted(['%foobar%', '%errors!'])
     assert (sorted(test_switch._get_errors(
         command_response=test_response)) == expected)
Пример #15
0
 def test_image_delete(self, mock_expectmore):
     """Expect this to try to delete the user requested firmware."""
     test_switch = SwitchMellanoxM7800(switch_ip_address='fakeip',
                                       password='******')
     firmware_name = 'my_amazing_firmware'
     test_switch.image_delete(image=firmware_name)
     mock_expectmore.return_value.ask.assert_called_with(
         f'image delete {firmware_name}')
Пример #16
0
	def test__get_relevant_responses_errors(self, mock_expectmore, test_driver):
		"""Test that SwitchException is raised on parsing errors."""
		test_switch = SwitchMellanoxM7800(switch_ip_address = 'fakeip', password = '******')
		with pytest.raises(SwitchException):
			test_switch._get_relevant_responses(
				command_response = test_driver.test_response,
				start_marker = test_driver.start,
				end_marker = test_driver.end,
			)
Пример #17
0
	def test__get_relevant_responses(self, mock_expectmore, test_driver):
		"""Test that only items between start_marker and end_marker are returned."""
		test_switch = SwitchMellanoxM7800(switch_ip_address = 'fakeip', password = '******')
		result = test_switch._get_relevant_responses(
			command_response = test_driver.test_response,
			start_marker = test_driver.start,
			end_marker = test_driver.end,
		)
		assert(result == test_driver.expected_output)
Пример #18
0
	def test_image_fetch_error_with_message(self, mock_expectmore):
		"""Expect an error to be raised due to a missing success indicator and the error message to be captured."""
		test_switch = SwitchMellanoxM7800(switch_ip_address = 'fakeip', password = '******')
		firmware_url = f'{test_switch.SUPPORTED_IMAGE_FETCH_PROTOCOLS[0]}://sometrustworthysite.ru'

		error_message = '% unauthorized'
		mock_expectmore.return_value.ask.return_value = ['other junk', error_message,]
		with pytest.raises(SwitchException, match=error_message) as exception:
			test_switch.image_fetch(url = firmware_url)
Пример #19
0
	def test_image_delete_error(self, mock_expectmore):
		"""Expect this to raise an error if there is an error response."""
		test_switch = SwitchMellanoxM7800(switch_ip_address = 'fakeip', password = '******')
		firmware_name = 'my_amazing_firmware'
		error_message = '% file not found'
		mock_expectmore.return_value.ask.return_value = [error_message]

		with pytest.raises(SwitchException, match=error_message) as exception:
			test_switch.image_delete(image = firmware_name)
Пример #20
0
	def test_disable_fallback_reboot(self, mock__get_errors, mock_expectmore):
		"""Expect that a fallback reboot command is asked and that the response is checked for any errors."""
		mock_expectmore.return_value.ask.return_value = 'testresults'
		mock__get_errors.return_value = []
		test_switch = SwitchMellanoxM7800(switch_ip_address = 'fakeip', password = '******')
		test_switch.disable_fallback_reboot()

		mock_expectmore.return_value.ask.assert_called_with('no boot next fallback-reboot enable')
		mock__get_errors.assert_called_with(command_response = mock_expectmore.return_value.ask.return_value)
Пример #21
0
	def test_connect_password_auth(self, mock_expectmore):
		"""Expect connect to authenticate using a password."""
		mock_expectmore.return_value.isalive.return_value = False
		# perform password auth
		mock_expectmore.return_value.match_index = 0
		test_switch = SwitchMellanoxM7800(switch_ip_address = 'fakeip', password = '******')
		test_switch.connect()
		# expect the password to be used
		mock_expectmore.return_value.say.assert_called_with(test_switch.password)
Пример #22
0
 def test__get_available_images_errors(self, mock_expectmore,
                                       test_response):
     """Test that an empty list is returned when no images are available."""
     test_console_response = test_response
     test_switch = SwitchMellanoxM7800(switch_ip_address='fakeip',
                                       password='******')
     with pytest.raises(SwitchException):
         test_switch._get_available_images(
             command_response=test_console_response)
Пример #23
0
	def test__get_available_images_no_images(self, mock_expectmore):
		"""Test that an empty list is returned when no images are available."""
		test_console_response = [
			'Next boot partition: 2',
			'No image files are available to be installed.',
			'Serve image files via HTTP/HTTPS: no'
		]
		test_switch = SwitchMellanoxM7800(switch_ip_address = 'fakeip', password = '******')
		assert(test_switch._get_available_images(command_response = test_console_response) == [])
Пример #24
0
	def test_connect_connection_failure(self, mock_expectmore):
		"""Expect a SwitchException to be raised when the initial connection prompt is not found."""
		mock_expectmore.return_value.isalive.return_value = False
		# Have wait raise the ExpectMoreException
		mock_expectmore.return_value.wait.side_effect = ExpectMoreException

		with pytest.raises(SwitchException):
			test_switch = SwitchMellanoxM7800(switch_ip_address = 'fakeip', password = '******')
			test_switch.connect()
Пример #25
0
 def test_factory_reset(self, mock_expectmore):
     """Expect the function to send the factory reset sequence."""
     test_switch = SwitchMellanoxM7800(switch_ip_address='fakeip',
                                       password='******')
     test_switch.factory_reset()
     mock_expectmore.return_value.conversation.assert_called_with([
         ('', 'reset factory'),
         ('reset:', 'YES'),
     ])
Пример #26
0
	def test_connect_already_alive(self, mock_expectmore):
		"""Test that connect short circuits if the process is already alive."""
		mock_expectmore.return_value.isalive.return_value = True
		test_switch = SwitchMellanoxM7800(switch_ip_address = 'fakeip', password = '******')
		test_switch.connect()
		# expect isalive to be checked
		mock_expectmore.return_value.isalive.assert_called_once()
		# expect no other calls
		assert(len(mock_expectmore.return_value.method_calls) == 1)
Пример #27
0
	def test_write_configuration(self, mock__get_errors, mock_expectmore):
		"""Expect that a write configuration command is asked and that the response is checked for any errors."""
		mock_expectmore.return_value.ask.return_value = 'testresults'
		mock__get_errors.return_value = []
		test_switch = SwitchMellanoxM7800(switch_ip_address = 'fakeip', password = '******')
		test_switch.write_configuration()

		mock_expectmore.return_value.ask.assert_called_with('configuration write')
		mock__get_errors.assert_called_with(command_response = mock_expectmore.return_value.ask.return_value)
Пример #28
0
	def test_image_fetch_bad_protocol(self, mock_expectmore):
		"""Expect an error to be raised on an unsupported protocol."""
		test_switch = SwitchMellanoxM7800(switch_ip_address = 'fakeip', password = '******')
		firmware_url = 'git://[email protected]/sometrustworthyrepo.git'
		# add success condition return value to ensure the exception is because of the
		# unsupported protocol.
		mock_expectmore.return_value.ask.return_value = ['100.0%']

		with pytest.raises(SwitchException):
			test_switch.image_fetch(url = firmware_url)
Пример #29
0
 def test_image_fetch(self, mock_expectmore, protocol):
     """Expect this to try to fetch the user requested firmware."""
     test_switch = SwitchMellanoxM7800(switch_ip_address='fakeip',
                                       password='******')
     firmware_url = f'{protocol}sometrustworthysite.ru'
     # add success condition return value
     mock_expectmore.return_value.ask.return_value = ['100.0%']
     test_switch.image_fetch(url=firmware_url)
     mock_expectmore.return_value.ask.assert_called_with(
         f'image fetch {firmware_url}', timeout=ANY)
Пример #30
0
	def test__get_installed_images(self, mock_expectmore, input_file, test_file):
		"""Test that images are found when present."""
		test_console_response = Path(test_file(input_file)).read_text().splitlines()

		expected_data = {
			1: 'X86_64 3.6.4006 2017-07-03 16:17:39 x86_64',
			2: 'X86_64 3.6.4006 2017-07-03 16:17:39 x86_64',
		}
		test_switch = SwitchMellanoxM7800(switch_ip_address = 'fakeip', password = '******')
		assert(test_switch._get_installed_images(command_response = test_console_response) == expected_data)