badfish 1.0.6__tar.gz → 1.1.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (60) hide show
  1. {badfish-1.0.6 → badfish-1.1.0}/PKG-INFO +4 -4
  2. {badfish-1.0.6 → badfish-1.1.0}/README.md +0 -1
  3. badfish-1.1.0/pyproject.toml +13 -0
  4. {badfish-1.0.6 → badfish-1.1.0}/setup.cfg +4 -3
  5. badfish-1.1.0/setup.py +18 -0
  6. badfish-1.1.0/src/badfish/__init__.py +1 -0
  7. {badfish-1.0.6 → badfish-1.1.0}/src/badfish/helpers/http_client.py +2 -2
  8. {badfish-1.0.6 → badfish-1.1.0}/src/badfish/main.py +11 -7
  9. {badfish-1.0.6 → badfish-1.1.0}/src/badfish.egg-info/PKG-INFO +4 -4
  10. {badfish-1.0.6 → badfish-1.1.0}/src/badfish.egg-info/SOURCES.txt +1 -1
  11. {badfish-1.0.6 → badfish-1.1.0}/src/badfish.egg-info/requires.txt +2 -1
  12. badfish-1.1.0/tests/test_async_loop.py +60 -0
  13. {badfish-1.0.6 → badfish-1.1.0}/tests/test_base.py +2 -2
  14. {badfish-1.0.6 → badfish-1.1.0}/tests/test_context_manager.py +10 -10
  15. {badfish-1.0.6 → badfish-1.1.0}/tests/test_custom_interfaces.py +1 -1
  16. {badfish-1.0.6 → badfish-1.1.0}/tests/test_execution.py +6 -6
  17. {badfish-1.0.6 → badfish-1.1.0}/tests/test_firmware_inventory.py +1 -1
  18. {badfish-1.0.6 → badfish-1.1.0}/tests/test_hosts_file.py +4 -4
  19. {badfish-1.0.6 → badfish-1.1.0}/tests/test_http_client.py +2 -2
  20. {badfish-1.0.6 → badfish-1.1.0}/tests/test_job_queue.py +2 -2
  21. {badfish-1.0.6 → badfish-1.1.0}/tests/test_logger.py +1 -1
  22. {badfish-1.0.6 → badfish-1.1.0}/tests/test_ls_interfaces.py +4 -4
  23. {badfish-1.0.6 → badfish-1.1.0}/tests/test_ls_memory.py +3 -3
  24. {badfish-1.0.6 → badfish-1.1.0}/tests/test_ls_processors.py +3 -3
  25. {badfish-1.0.6 → badfish-1.1.0}/tests/test_ls_serial.py +4 -4
  26. {badfish-1.0.6 → badfish-1.1.0}/tests/test_nic_attributes.py +4 -4
  27. {badfish-1.0.6 → badfish-1.1.0}/tests/test_power.py +2 -2
  28. {badfish-1.0.6 → badfish-1.1.0}/tests/test_power_consumed_watts.py +1 -1
  29. {badfish-1.0.6 → badfish-1.1.0}/tests/test_reboot_only.py +1 -1
  30. {badfish-1.0.6 → badfish-1.1.0}/tests/test_scp.py +2 -2
  31. badfish-1.0.6/pyproject.toml +0 -6
  32. badfish-1.0.6/setup.py +0 -5
  33. badfish-1.0.6/src/badfish/__init__.py +0 -0
  34. badfish-1.0.6/src/badfish/helpers/async_lru.py +0 -215
  35. {badfish-1.0.6 → badfish-1.1.0}/LICENSE +0 -0
  36. {badfish-1.0.6 → badfish-1.1.0}/src/badfish/config.py +0 -0
  37. {badfish-1.0.6 → badfish-1.1.0}/src/badfish/helpers/__init__.py +0 -0
  38. {badfish-1.0.6 → badfish-1.1.0}/src/badfish/helpers/exceptions.py +0 -0
  39. {badfish-1.0.6 → badfish-1.1.0}/src/badfish/helpers/logger.py +0 -0
  40. {badfish-1.0.6 → badfish-1.1.0}/src/badfish/helpers/parser.py +0 -0
  41. {badfish-1.0.6 → badfish-1.1.0}/src/badfish.egg-info/dependency_links.txt +0 -0
  42. {badfish-1.0.6 → badfish-1.1.0}/src/badfish.egg-info/entry_points.txt +0 -0
  43. {badfish-1.0.6 → badfish-1.1.0}/src/badfish.egg-info/top_level.txt +0 -0
  44. {badfish-1.0.6 → badfish-1.1.0}/src/badfish.egg-info/zip-safe +0 -0
  45. {badfish-1.0.6 → badfish-1.1.0}/tests/test_bios_attributes.py +0 -0
  46. {badfish-1.0.6 → badfish-1.1.0}/tests/test_bios_password.py +0 -0
  47. {badfish-1.0.6 → badfish-1.1.0}/tests/test_boot_to.py +0 -0
  48. {badfish-1.0.6 → badfish-1.1.0}/tests/test_boot_to_mac.py +0 -0
  49. {badfish-1.0.6 → badfish-1.1.0}/tests/test_boot_to_type.py +0 -0
  50. {badfish-1.0.6 → badfish-1.1.0}/tests/test_change_boot.py +0 -0
  51. {badfish-1.0.6 → badfish-1.1.0}/tests/test_check_boot.py +0 -0
  52. {badfish-1.0.6 → badfish-1.1.0}/tests/test_ls_gpu.py +0 -0
  53. {badfish-1.0.6 → badfish-1.1.0}/tests/test_main_coverage.py +0 -0
  54. {badfish-1.0.6 → badfish-1.1.0}/tests/test_next_boot_pxe.py +0 -0
  55. {badfish-1.0.6 → badfish-1.1.0}/tests/test_reset_bios.py +0 -0
  56. {badfish-1.0.6 → badfish-1.1.0}/tests/test_reset_bmc.py +0 -0
  57. {badfish-1.0.6 → badfish-1.1.0}/tests/test_reset_idrac.py +0 -0
  58. {badfish-1.0.6 → badfish-1.1.0}/tests/test_screenshot.py +0 -0
  59. {badfish-1.0.6 → badfish-1.1.0}/tests/test_sriov_mode.py +0 -0
  60. {badfish-1.0.6 → badfish-1.1.0}/tests/test_virtual_media.py +0 -0
@@ -1,11 +1,11 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: badfish
3
- Version: 1.0.6
3
+ Version: 1.1.0
4
4
  Summary: Badfish is a Redfish-based API tool for managing bare-metal systems via the Redfish API
5
5
  Home-page: https://github.com/redhat-performance/badfish
6
6
  Author: Gonzalo Rafuls
7
7
  Author-email: gonza@redhat.com
8
- License: GPLv3
8
+ License: GPL-3.0-or-later
9
9
  Project-URL: Bug Tracker, https://github.com/redhat-performance/badfish/issues
10
10
  Project-URL: Documentation, https://github.com/redhat-performance/badfish/blob/master/README.md
11
11
  Project-URL: Source Code, https://github.com/redhat-performance/badfish
@@ -24,8 +24,9 @@ Requires-Python: >=3.10
24
24
  Description-Content-Type: text/markdown
25
25
  License-File: LICENSE
26
26
  Requires-Dist: pyyaml>=3.10
27
- Requires-Dist: aiohttp>=3.7.4
27
+ Requires-Dist: aiohttp>=3.10
28
28
  Requires-Dist: setuptools>=39.0
29
+ Requires-Dist: async-lru>=2.0
29
30
  Dynamic: license-file
30
31
 
31
32
  <p align="center">
@@ -753,7 +754,6 @@ With rack, ULocation and blade being optional in a hierarchical fashion otherwis
753
754
 
754
755
  Please refer to our contributing [guide](CONTRIBUTING.md).
755
756
 
756
-
757
757
  * Here is some useful documentation
758
758
  - [Creating a pull request](https://help.github.com/en/github/collaborating-with-issues-and-pull-requests/creating-a-pull-request)
759
759
  - [Keeping a cloned fork up to date](https://help.github.com/en/github/collaborating-with-issues-and-pull-requests/syncing-a-fork)
@@ -723,7 +723,6 @@ With rack, ULocation and blade being optional in a hierarchical fashion otherwis
723
723
 
724
724
  Please refer to our contributing [guide](CONTRIBUTING.md).
725
725
 
726
-
727
726
  * Here is some useful documentation
728
727
  - [Creating a pull request](https://help.github.com/en/github/collaborating-with-issues-and-pull-requests/creating-a-pull-request)
729
728
  - [Keeping a cloned fork up to date](https://help.github.com/en/github/collaborating-with-issues-and-pull-requests/syncing-a-fork)
@@ -0,0 +1,13 @@
1
+ [build-system]
2
+ requires = ["setuptools>=39", "wheel"]
3
+ build-backend = "setuptools.build_meta"
4
+
5
+ [tool.black]
6
+ line-length = 120
7
+
8
+ [tool.semantic_release]
9
+ version_variables = [
10
+ "src/badfish/__init__.py:__version__"
11
+ ]
12
+ branch = "master"
13
+ build_command = "pip install build && python -m build"
@@ -5,7 +5,7 @@ author_email = gonza@redhat.com
5
5
  description = Badfish is a Redfish-based API tool for managing bare-metal systems via the Redfish API
6
6
  long_description = file: README.md
7
7
  long_description_content_type = text/markdown
8
- license = GPLv3
8
+ license = GPL-3.0-or-later
9
9
  license_file = LICENSE
10
10
  platforms = any
11
11
  url = https://github.com/redhat-performance/badfish
@@ -30,10 +30,11 @@ packages = find:
30
30
  python_requires = >=3.10
31
31
  install_requires =
32
32
  pyyaml>=3.10
33
- aiohttp>=3.7.4
33
+ aiohttp>=3.10
34
34
  setuptools>=39.0
35
+ async-lru>=2.0
35
36
  package_dir =
36
- =src
37
+ = src
37
38
  zip_safe = True
38
39
 
39
40
  [options.packages.find]
badfish-1.1.0/setup.py ADDED
@@ -0,0 +1,18 @@
1
+ import setuptools
2
+ import re
3
+ import os
4
+
5
+ here = os.path.abspath(os.path.dirname(__file__))
6
+ version_file = os.path.join(here, "src", "badfish", "__init__.py")
7
+
8
+ with open(version_file, "r", encoding="utf-8") as f:
9
+ content = f.read()
10
+ match = re.search(r"^__version__ = ['\"]([^'\"]*)['\"]", content, re.M)
11
+ if match:
12
+ current_version = match.group(1)
13
+ else:
14
+ raise RuntimeError("Unable to find version string in src/badfish/__init__.py")
15
+
16
+ setuptools.setup(
17
+ version=current_version
18
+ )
@@ -0,0 +1 @@
1
+ __version__ = "1.1.0"
@@ -4,8 +4,8 @@ from typing import Any, Dict, Optional
4
4
 
5
5
  import aiohttp
6
6
 
7
- from src.badfish.helpers.async_lru import alru_cache
8
- from src.badfish.helpers.exceptions import BadfishException
7
+ from async_lru import alru_cache
8
+ from badfish.helpers.exceptions import BadfishException
9
9
 
10
10
 
11
11
  class HTTPClient:
@@ -13,11 +13,11 @@ import yaml
13
13
  import tempfile
14
14
  from urllib.parse import urlparse
15
15
 
16
- from src.badfish.helpers import get_now
17
- from src.badfish.helpers.parser import parse_arguments
18
- from src.badfish.helpers.logger import BadfishLogger
19
- from src.badfish.helpers.http_client import HTTPClient
20
- from src.badfish.helpers.exceptions import BadfishException
16
+ from badfish.helpers import get_now
17
+ from badfish.helpers.parser import parse_arguments
18
+ from badfish.helpers.logger import BadfishLogger
19
+ from badfish.helpers.http_client import HTTPClient
20
+ from badfish.helpers.exceptions import BadfishException
21
21
 
22
22
  from logging import (
23
23
  DEBUG,
@@ -393,7 +393,7 @@ class Badfish:
393
393
 
394
394
  _uri = "%s%s" % (self.host_uri, session_uri)
395
395
  check_response = await self.http_client.get_request(_uri, _continue=True, _get_token=True)
396
- if check_response is None:
396
+ if check_response is None or check_response.status != 200:
397
397
  session_uri = "/redfish/v1/SessionService/Sessions"
398
398
 
399
399
  return session_uri
@@ -2674,7 +2674,11 @@ def main(argv=None):
2674
2674
  output = _args["output"]
2675
2675
  bfl = BadfishLogger(_args["verbose"], multi_host, _args["log"], output)
2676
2676
 
2677
- loop = asyncio.get_event_loop()
2677
+ try:
2678
+ loop = asyncio.get_event_loop()
2679
+ except RuntimeError:
2680
+ loop = asyncio.new_event_loop()
2681
+ asyncio.set_event_loop(loop)
2678
2682
  tasks = []
2679
2683
  host_order = {}
2680
2684
  if host_list:
@@ -1,11 +1,11 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: badfish
3
- Version: 1.0.6
3
+ Version: 1.1.0
4
4
  Summary: Badfish is a Redfish-based API tool for managing bare-metal systems via the Redfish API
5
5
  Home-page: https://github.com/redhat-performance/badfish
6
6
  Author: Gonzalo Rafuls
7
7
  Author-email: gonza@redhat.com
8
- License: GPLv3
8
+ License: GPL-3.0-or-later
9
9
  Project-URL: Bug Tracker, https://github.com/redhat-performance/badfish/issues
10
10
  Project-URL: Documentation, https://github.com/redhat-performance/badfish/blob/master/README.md
11
11
  Project-URL: Source Code, https://github.com/redhat-performance/badfish
@@ -24,8 +24,9 @@ Requires-Python: >=3.10
24
24
  Description-Content-Type: text/markdown
25
25
  License-File: LICENSE
26
26
  Requires-Dist: pyyaml>=3.10
27
- Requires-Dist: aiohttp>=3.7.4
27
+ Requires-Dist: aiohttp>=3.10
28
28
  Requires-Dist: setuptools>=39.0
29
+ Requires-Dist: async-lru>=2.0
29
30
  Dynamic: license-file
30
31
 
31
32
  <p align="center">
@@ -753,7 +754,6 @@ With rack, ULocation and blade being optional in a hierarchical fashion otherwis
753
754
 
754
755
  Please refer to our contributing [guide](CONTRIBUTING.md).
755
756
 
756
-
757
757
  * Here is some useful documentation
758
758
  - [Creating a pull request](https://help.github.com/en/github/collaborating-with-issues-and-pull-requests/creating-a-pull-request)
759
759
  - [Keeping a cloned fork up to date](https://help.github.com/en/github/collaborating-with-issues-and-pull-requests/syncing-a-fork)
@@ -14,11 +14,11 @@ src/badfish.egg-info/requires.txt
14
14
  src/badfish.egg-info/top_level.txt
15
15
  src/badfish.egg-info/zip-safe
16
16
  src/badfish/helpers/__init__.py
17
- src/badfish/helpers/async_lru.py
18
17
  src/badfish/helpers/exceptions.py
19
18
  src/badfish/helpers/http_client.py
20
19
  src/badfish/helpers/logger.py
21
20
  src/badfish/helpers/parser.py
21
+ tests/test_async_loop.py
22
22
  tests/test_base.py
23
23
  tests/test_bios_attributes.py
24
24
  tests/test_bios_password.py
@@ -1,3 +1,4 @@
1
1
  pyyaml>=3.10
2
- aiohttp>=3.7.4
2
+ aiohttp>=3.10
3
3
  setuptools>=39.0
4
+ async-lru>=2.0
@@ -0,0 +1,60 @@
1
+ import unittest
2
+ from unittest.mock import patch, MagicMock
3
+ from badfish.main import main
4
+
5
+
6
+ class TestAsyncioFix(unittest.TestCase):
7
+ @patch('badfish.main.execute_badfish')
8
+ @patch('badfish.main.BadfishLogger')
9
+ @patch('badfish.main.parse_arguments')
10
+ @patch('asyncio.set_event_loop')
11
+ @patch('asyncio.new_event_loop')
12
+ @patch('asyncio.get_event_loop')
13
+ def test_main_handles_no_event_loop(self, mock_get_loop, mock_new_loop,
14
+ mock_set_loop, mock_parse_args,
15
+ mock_logger, mock_execute):
16
+ mock_get_loop.side_effect = RuntimeError("No event loop")
17
+
18
+ mock_loop_instance = MagicMock()
19
+ mock_new_loop.return_value = mock_loop_instance
20
+ mock_loop_instance.run_until_complete.return_value = ("localhost", True)
21
+
22
+ mock_parse_args.return_value = {
23
+ "verbose": False, "host": "localhost", "delta": None,
24
+ "firmware_inventory": None, "host_list": None, "log": None,
25
+ "output": None
26
+ }
27
+
28
+ main()
29
+
30
+ mock_get_loop.assert_called_once()
31
+ mock_new_loop.assert_called_once()
32
+ mock_set_loop.assert_called_once_with(mock_loop_instance)
33
+ mock_loop_instance.run_until_complete.assert_called()
34
+
35
+ @patch('badfish.main.execute_badfish')
36
+ @patch('badfish.main.BadfishLogger')
37
+ @patch('badfish.main.parse_arguments')
38
+ @patch('asyncio.set_event_loop')
39
+ @patch('asyncio.new_event_loop')
40
+ @patch('asyncio.get_event_loop')
41
+ def test_main_uses_existing_loop(self, mock_get_loop, mock_new_loop,
42
+ mock_set_loop, mock_parse_args,
43
+ mock_logger, mock_execute):
44
+ existing_loop = MagicMock()
45
+ mock_get_loop.return_value = existing_loop
46
+ mock_get_loop.side_effect = None
47
+ existing_loop.run_until_complete.return_value = ("localhost", True)
48
+
49
+ mock_parse_args.return_value = {
50
+ "verbose": False, "host": "localhost", "delta": None,
51
+ "firmware_inventory": None, "host_list": None, "log": None,
52
+ "output": None
53
+ }
54
+
55
+ main()
56
+
57
+ mock_get_loop.assert_called_once()
58
+ mock_new_loop.assert_not_called()
59
+ mock_set_loop.assert_not_called()
60
+ existing_loop.run_until_complete.assert_called()
@@ -5,8 +5,8 @@ import pytest
5
5
  from aiohttp import web
6
6
  from aiohttp.test_utils import AioHTTPTestCase
7
7
 
8
- from src.badfish.main import main
9
- from src.badfish.helpers.exceptions import BadfishException
8
+ from badfish.main import main
9
+ from badfish.helpers.exceptions import BadfishException
10
10
  from tests import config
11
11
 
12
12
 
@@ -1,7 +1,7 @@
1
1
  import pytest
2
2
  from unittest.mock import AsyncMock, patch, MagicMock
3
- from src.badfish.main import Badfish, badfish_factory
4
- from src.badfish.helpers.exceptions import BadfishException
3
+ from badfish.main import Badfish, badfish_factory
4
+ from badfish.helpers.exceptions import BadfishException
5
5
 
6
6
 
7
7
  class MockLogger:
@@ -386,7 +386,7 @@ class TestExecuteBadfishSessionCleanup:
386
386
  @pytest.mark.asyncio
387
387
  async def test_execute_badfish_session_cleanup_success(self):
388
388
  """Test successful session cleanup in execute_badfish."""
389
- from src.badfish.main import execute_badfish
389
+ from badfish.main import execute_badfish
390
390
 
391
391
  logger = MockLogger()
392
392
  args = {
@@ -451,7 +451,7 @@ class TestExecuteBadfishSessionCleanup:
451
451
  "set_nic_attribute": None,
452
452
  }
453
453
 
454
- with patch("src.badfish.main.badfish_factory") as mock_factory:
454
+ with patch("badfish.main.badfish_factory") as mock_factory:
455
455
  # Mock badfish instance
456
456
  mock_badfish = MagicMock()
457
457
  mock_badfish.session_id = "/redfish/v1/SessionService/Sessions/123"
@@ -469,7 +469,7 @@ class TestExecuteBadfishSessionCleanup:
469
469
  @pytest.mark.asyncio
470
470
  async def test_execute_badfish_session_cleanup_failure(self):
471
471
  """Test session cleanup failure in execute_badfish."""
472
- from src.badfish.main import execute_badfish
472
+ from badfish.main import execute_badfish
473
473
 
474
474
  logger = MockLogger()
475
475
  args = {
@@ -534,7 +534,7 @@ class TestExecuteBadfishSessionCleanup:
534
534
  "set_nic_attribute": None,
535
535
  }
536
536
 
537
- with patch("src.badfish.main.badfish_factory") as mock_factory:
537
+ with patch("badfish.main.badfish_factory") as mock_factory:
538
538
  # Mock badfish instance
539
539
  mock_badfish = MagicMock()
540
540
  mock_badfish.session_id = "/redfish/v1/SessionService/Sessions/123"
@@ -556,7 +556,7 @@ class TestExecuteBadfishSessionCleanup:
556
556
  @pytest.mark.asyncio
557
557
  async def test_execute_badfish_no_session_cleanup(self):
558
558
  """Test execute_badfish when no session exists to clean up."""
559
- from src.badfish.main import execute_badfish
559
+ from badfish.main import execute_badfish
560
560
 
561
561
  logger = MockLogger()
562
562
  args = {
@@ -621,7 +621,7 @@ class TestExecuteBadfishSessionCleanup:
621
621
  "set_nic_attribute": None,
622
622
  }
623
623
 
624
- with patch("src.badfish.main.badfish_factory") as mock_factory:
624
+ with patch("badfish.main.badfish_factory") as mock_factory:
625
625
  # Mock badfish instance with no session_id
626
626
  mock_badfish = MagicMock()
627
627
  mock_badfish.session_id = None
@@ -639,7 +639,7 @@ class TestExecuteBadfishSessionCleanup:
639
639
  @pytest.mark.asyncio
640
640
  async def test_execute_badfish_no_badfish_instance(self):
641
641
  """Test execute_badfish when badfish instance is None."""
642
- from src.badfish.main import execute_badfish
642
+ from badfish.main import execute_badfish
643
643
 
644
644
  logger = MockLogger()
645
645
  args = {
@@ -704,7 +704,7 @@ class TestExecuteBadfishSessionCleanup:
704
704
  "set_nic_attribute": None,
705
705
  }
706
706
 
707
- with patch("src.badfish.main.badfish_factory") as mock_factory:
707
+ with patch("badfish.main.badfish_factory") as mock_factory:
708
708
  # Mock badfish_factory to raise an exception
709
709
  mock_factory.side_effect = BadfishException("Connection failed")
710
710
 
@@ -1,6 +1,6 @@
1
1
  import pytest
2
2
 
3
- from src.badfish.main import Badfish
3
+ from badfish.main import Badfish
4
4
  from tests.config import INTERFACES_PATH
5
5
  from tests.test_base import TestBase
6
6
 
@@ -1,7 +1,7 @@
1
1
  import os
2
2
  from unittest.mock import patch
3
3
 
4
- from src.badfish.helpers.exceptions import BadfishException
4
+ from badfish.helpers.exceptions import BadfishException
5
5
  from tests.config import (
6
6
  HOST_LIST_EXTRAS,
7
7
  KEYBOARD_INTERRUPT,
@@ -30,12 +30,12 @@ def raise_badfish_exception_stub(ignore1, ignore2, ignore3, ignore4=None):
30
30
  class TestSingleHostExecution(TestBase):
31
31
  args = ["--ls-jobs"]
32
32
 
33
- @patch("src.badfish.main.execute_badfish", raise_keyb_interrupt_stub)
33
+ @patch("badfish.main.execute_badfish", raise_keyb_interrupt_stub)
34
34
  def test_single_host_keyb_interrupt(self):
35
35
  _, err = self.badfish_call()
36
36
  assert err == KEYBOARD_INTERRUPT
37
37
 
38
- @patch("src.badfish.main.execute_badfish", raise_badfish_exception_stub)
38
+ @patch("badfish.main.execute_badfish", raise_badfish_exception_stub)
39
39
  def test_single_host_badfish_exception(self):
40
40
  _, err = self.badfish_call()
41
41
  assert err == WRONG_BADFISH_EXECUTION
@@ -52,17 +52,17 @@ class TestHostListExecution(TestBase):
52
52
  "--ls-jobs",
53
53
  ]
54
54
 
55
- @patch("src.badfish.main.execute_badfish", raise_keyb_interrupt_stub)
55
+ @patch("badfish.main.execute_badfish", raise_keyb_interrupt_stub)
56
56
  def test_host_list_keyb_interrupt(self):
57
57
  _, err = self.badfish_call(mock_host=None)
58
58
  assert err == KEYBOARD_INTERRUPT_HOST_LIST
59
59
 
60
- @patch("src.badfish.main.execute_badfish", raise_badfish_exception_stub)
60
+ @patch("badfish.main.execute_badfish", raise_badfish_exception_stub)
61
61
  def test_host_list_badfish_exception(self):
62
62
  _, err = self.badfish_call(mock_host=None)
63
63
  assert err == WRONG_BADFISH_EXECUTION_HOST_LIST
64
64
 
65
- @patch("src.badfish.main.execute_badfish")
65
+ @patch("badfish.main.execute_badfish")
66
66
  def test_host_list_successful(self, mock_execute):
67
67
  mock_execute.return_value = "Successful."
68
68
  _, err = self.badfish_call(mock_host=None)
@@ -62,7 +62,7 @@ class TestFirmwareInventory(TestBase):
62
62
  @patch("aiohttp.ClientSession.delete")
63
63
  @patch("aiohttp.ClientSession.post")
64
64
  @patch("aiohttp.ClientSession.get")
65
- @patch("src.badfish.main.Badfish.get_request")
65
+ @patch("badfish.main.Badfish.get_request")
66
66
  def test_firmware_inventory_none_response(self, mock_get_req_call, mock_get, mock_post, mock_delete):
67
67
  mock_get_req_call.side_effect = [
68
68
  MockResponse(FIRMWARE_INVENTORY_RESP, 200), # Firmware inventory list
@@ -16,7 +16,7 @@ class TestHostsFile(TestBase):
16
16
  def test_hosts_good(self):
17
17
  self.args = [self.option_arg, self.mock_hosts_good_path]
18
18
 
19
- with patch("src.badfish.main.execute_badfish") as badfish_mock:
19
+ with patch("badfish.main.execute_badfish") as badfish_mock:
20
20
  self.badfish_call(mock_host=None)
21
21
 
22
22
  badfish_mock.assert_awaited()
@@ -32,7 +32,7 @@ class TestHostsFile(TestBase):
32
32
  def test_hosts_non_existent(self):
33
33
  self.args = [self.option_arg, "non/existent/file"]
34
34
 
35
- with patch("src.badfish.main.execute_badfish") as badfish_mock:
35
+ with patch("badfish.main.execute_badfish") as badfish_mock:
36
36
  out, err = self.badfish_call(mock_host=None)
37
37
  badfish_mock.assert_not_awaited()
38
38
 
@@ -44,7 +44,7 @@ class TestHostsFile(TestBase):
44
44
  """
45
45
  self.args = [self.option_arg, self.mock_hosts_empty_path]
46
46
 
47
- with patch("src.badfish.main.execute_badfish") as badfish_mock:
47
+ with patch("badfish.main.execute_badfish") as badfish_mock:
48
48
  result = self.badfish_call(mock_host=None)
49
49
 
50
50
  badfish_mock.assert_not_awaited()
@@ -53,7 +53,7 @@ class TestHostsFile(TestBase):
53
53
  def test_hosts_bad(self):
54
54
  self.args = [self.option_arg, self.mock_hosts_garbled_path]
55
55
 
56
- with patch("src.badfish.main.execute_badfish") as badfish_mock:
56
+ with patch("badfish.main.execute_badfish") as badfish_mock:
57
57
  self.badfish_call(mock_host=None)
58
58
 
59
59
  badfish_mock.assert_awaited()
@@ -5,8 +5,8 @@ from unittest.mock import AsyncMock, MagicMock, PropertyMock, patch
5
5
  import aiohttp
6
6
  import pytest
7
7
 
8
- from src.badfish.helpers.http_client import HTTPClient
9
- from src.badfish.helpers.exceptions import BadfishException
8
+ from badfish.helpers.http_client import HTTPClient
9
+ from badfish.helpers.exceptions import BadfishException
10
10
 
11
11
 
12
12
  class DummyLogger:
@@ -1,6 +1,6 @@
1
1
  from unittest.mock import patch
2
2
 
3
- from src.badfish.helpers.exceptions import BadfishException
3
+ from badfish.helpers.exceptions import BadfishException
4
4
  from tests.config import (
5
5
  BLANK_RESP,
6
6
  INIT_RESP,
@@ -204,7 +204,7 @@ class TestCheckJob(TestBase):
204
204
  @patch("aiohttp.ClientSession.delete")
205
205
  @patch("aiohttp.ClientSession.post")
206
206
  @patch("aiohttp.ClientSession.get")
207
- @patch("src.badfish.main.Badfish.get_request")
207
+ @patch("badfish.main.Badfish.get_request")
208
208
  def test_check_job_error(self, mock_get_req_call, mock_get, mock_post, mock_delete):
209
209
  # The check_schedule_job_status method only makes one call to get_request
210
210
  # which should return None to simulate the error condition
@@ -2,7 +2,7 @@ import os
2
2
  import tempfile
3
3
  from logging import INFO, ERROR, DEBUG, LogRecord
4
4
 
5
- from src.badfish.helpers.logger import BadfishHandler, BadfishLogger
5
+ from badfish.helpers.logger import BadfishHandler, BadfishLogger
6
6
  import yaml
7
7
  from unittest.mock import patch
8
8
 
@@ -61,8 +61,8 @@ class TestLsInterfaces(TestBase):
61
61
  @patch("aiohttp.ClientSession.delete")
62
62
  @patch("aiohttp.ClientSession.post")
63
63
  @patch("aiohttp.ClientSession.get")
64
- @patch("src.badfish.main.Badfish.check_supported_network_interfaces")
65
- @patch("src.badfish.main.Badfish.get_ethernet_interfaces")
64
+ @patch("badfish.main.Badfish.check_supported_network_interfaces")
65
+ @patch("badfish.main.Badfish.get_ethernet_interfaces")
66
66
  def test_ls_interfaces_ethernet(self, mock_get_ethernet, mock_check_support, mock_get, mock_post, mock_delete):
67
67
  # Mock the support checks: NetworkAdapters not supported, EthernetInterfaces supported
68
68
  mock_check_support.side_effect = [False, True]
@@ -94,7 +94,7 @@ class TestLsInterfaces(TestBase):
94
94
  @patch("aiohttp.ClientSession.delete")
95
95
  @patch("aiohttp.ClientSession.post")
96
96
  @patch("aiohttp.ClientSession.get")
97
- @patch("src.badfish.main.Badfish.check_supported_network_interfaces")
97
+ @patch("badfish.main.Badfish.check_supported_network_interfaces")
98
98
  def test_ls_interfaces_ethernet_not_supported(self, mock_check_support, mock_get, mock_post, mock_delete):
99
99
  # Mock the support checks: both NetworkAdapters and EthernetInterfaces not supported
100
100
  mock_check_support.side_effect = [False, False]
@@ -125,7 +125,7 @@ class TestLsInterfaces(TestBase):
125
125
  @patch("aiohttp.ClientSession.delete")
126
126
  @patch("aiohttp.ClientSession.post")
127
127
  @patch("aiohttp.ClientSession.get")
128
- @patch("src.badfish.main.Badfish.check_supported_network_interfaces")
128
+ @patch("badfish.main.Badfish.check_supported_network_interfaces")
129
129
  def test_ls_interfaces_none_supported(self, mock_check_support, mock_get, mock_post, mock_delete):
130
130
  # Mock the support checks: both NetworkAdapters and EthernetInterfaces not supported
131
131
  mock_check_support.side_effect = [False, False]
@@ -68,12 +68,12 @@ class TestLsMemory(TestBase):
68
68
  @patch("aiohttp.ClientSession.delete")
69
69
  @patch("aiohttp.ClientSession.post")
70
70
  @patch("aiohttp.ClientSession.get")
71
- @patch("src.badfish.main.Badfish.get_memory_summary")
72
- @patch("src.badfish.main.Badfish.get_memory_details")
71
+ @patch("badfish.main.Badfish.get_memory_summary")
72
+ @patch("badfish.main.Badfish.get_memory_details")
73
73
  def test_ls_memory_details_not_found(
74
74
  self, mock_get_mem_details, mock_get_mem_summary, mock_get, mock_post, mock_delete
75
75
  ):
76
- from src.badfish.main import BadfishException
76
+ from badfish.main import BadfishException
77
77
 
78
78
  # Mock successful memory summary
79
79
  memory_summary = {"MemoryMirroring": "System", "TotalSystemMemoryGiB": 384}
@@ -69,12 +69,12 @@ class TestLsProcessors(TestBase):
69
69
  @patch("aiohttp.ClientSession.delete")
70
70
  @patch("aiohttp.ClientSession.post")
71
71
  @patch("aiohttp.ClientSession.get")
72
- @patch("src.badfish.main.Badfish.get_processor_summary")
73
- @patch("src.badfish.main.Badfish.get_processor_details")
72
+ @patch("badfish.main.Badfish.get_processor_summary")
73
+ @patch("badfish.main.Badfish.get_processor_details")
74
74
  def test_ls_processors_details_not_found(
75
75
  self, mock_get_proc_details, mock_get_proc_summary, mock_get, mock_post, mock_delete
76
76
  ):
77
- from src.badfish.main import BadfishException
77
+ from badfish.main import BadfishException
78
78
 
79
79
  # Mock successful processor summary (the data from PROCESSOR_SUMMARY_RESP)
80
80
  processor_summary = {
@@ -41,9 +41,9 @@ class TestLsSerial(TestBase):
41
41
  @patch("aiohttp.ClientSession.delete")
42
42
  @patch("aiohttp.ClientSession.post")
43
43
  @patch("aiohttp.ClientSession.get")
44
- @patch("src.badfish.main.Badfish.get_serial_summary")
44
+ @patch("badfish.main.Badfish.get_serial_summary")
45
45
  def test_ls_serial_unsupported_root(self, mock_get_serial_summary, mock_get, mock_post, mock_delete):
46
- from src.badfish.main import BadfishException
46
+ from badfish.main import BadfishException
47
47
 
48
48
  # Mock serial summary to raise the expected exception
49
49
  mock_get_serial_summary.side_effect = BadfishException("Server does not support this functionality")
@@ -57,9 +57,9 @@ class TestLsSerial(TestBase):
57
57
  @patch("aiohttp.ClientSession.delete")
58
58
  @patch("aiohttp.ClientSession.post")
59
59
  @patch("aiohttp.ClientSession.get")
60
- @patch("src.badfish.main.Badfish.get_serial_summary")
60
+ @patch("badfish.main.Badfish.get_serial_summary")
61
61
  def test_ls_serial_unsupported_systems(self, mock_get_serial_summary, mock_get, mock_post, mock_delete):
62
- from src.badfish.main import BadfishException
62
+ from badfish.main import BadfishException
63
63
 
64
64
  # Mock serial summary to raise the expected exception
65
65
  mock_get_serial_summary.side_effect = BadfishException("Server does not support this functionality")
@@ -124,9 +124,9 @@ class TestGetNICAttribute(TestBase):
124
124
  @patch("aiohttp.ClientSession.delete")
125
125
  @patch("aiohttp.ClientSession.post")
126
126
  @patch("aiohttp.ClientSession.get")
127
- @patch("src.badfish.main.Badfish.get_nic_attribute")
127
+ @patch("badfish.main.Badfish.get_nic_attribute")
128
128
  def test_get_nic_attr_list_unsupported(self, mock_get_nic_attr, mock_get, mock_post, mock_delete):
129
- from src.badfish.main import BadfishException
129
+ from badfish.main import BadfishException
130
130
 
131
131
  # Mock get_nic_attribute to raise BadfishException with vendor unsupported message
132
132
  mock_get_nic_attr.side_effect = BadfishException("Operation not supported by vendor.")
@@ -172,14 +172,14 @@ class TestGetNICAttribute(TestBase):
172
172
  @patch("aiohttp.ClientSession.delete")
173
173
  @patch("aiohttp.ClientSession.post")
174
174
  @patch("aiohttp.ClientSession.get")
175
- @patch("src.badfish.main.Badfish.get_idrac_fw_version")
175
+ @patch("badfish.main.Badfish.get_idrac_fw_version")
176
176
  def test_get_nic_attr_fw_bad(self, mock_get_fw, mock_get, mock_post, mock_delete):
177
177
 
178
178
  async def fake_get_fw():
179
179
  # Emit via Badfish logger name to match formatting
180
180
  from logging import getLogger
181
181
 
182
- getLogger("src.badfish.helpers.logger").error("Operation not supported by vendor.")
182
+ getLogger("badfish.helpers.logger").error("Operation not supported by vendor.")
183
183
  return 0
184
184
 
185
185
  mock_get_fw.side_effect = fake_get_fw
@@ -89,7 +89,7 @@ class TestPowerOff(TestBase):
89
89
  @patch("aiohttp.ClientSession.delete")
90
90
  @patch("aiohttp.ClientSession.post")
91
91
  @patch("aiohttp.ClientSession.get")
92
- @patch("src.badfish.main.Badfish.get_request")
92
+ @patch("badfish.main.Badfish.get_request")
93
93
  def test_power_off_none(self, mock_get_req_call, mock_get, mock_post, mock_delete):
94
94
  # The power off operation should return None when getting power state
95
95
  mock_get_req_call.side_effect = [None]
@@ -139,7 +139,7 @@ class TestPowerState(TestBase):
139
139
  @patch("aiohttp.ClientSession.delete")
140
140
  @patch("aiohttp.ClientSession.post")
141
141
  @patch("aiohttp.ClientSession.get")
142
- @patch("src.badfish.main.Badfish.get_request")
142
+ @patch("badfish.main.Badfish.get_request")
143
143
  def test_power_state_none(self, mock_get_req_call, mock_get, mock_post, mock_delete):
144
144
  # The power state check should return None (simulating communication failure)
145
145
  mock_get_req_call.side_effect = [None]
@@ -31,7 +31,7 @@ class TestPowerConsumed(TestBase):
31
31
  @patch("aiohttp.ClientSession.get")
32
32
  def test_power_consumed_404(self, mock_get, mock_post, mock_delete):
33
33
  # Mock the get_request method to return a 404 response that properly triggers vendor error
34
- with patch("src.badfish.main.Badfish.get_request") as mock_get_request:
34
+ with patch("badfish.main.Badfish.get_request") as mock_get_request:
35
35
  from tests.test_base import MockResponse
36
36
 
37
37
  # Mock get_request to return 404 for power endpoint
@@ -90,7 +90,7 @@ class TestRebootOnly(TestBase):
90
90
  _, err = self.badfish_call()
91
91
  assert err == RESPONSE_REBOOT_ONLY_SUCCESS_WITH_NG_RT
92
92
 
93
- @patch("src.badfish.main.RETRIES", 0)
93
+ @patch("badfish.main.RETRIES", 0)
94
94
  @patch("aiohttp.ClientSession.delete")
95
95
  @patch("aiohttp.ClientSession.get")
96
96
  @patch("aiohttp.ClientSession.post")
@@ -164,7 +164,7 @@ class TestExportSCP(TestBase):
164
164
  _, err = self.badfish_call()
165
165
  assert err == RESPONSE_EXPORT_SCP_NO_LOCATION
166
166
 
167
- @patch("src.badfish.main.get_now", fixed_datetime)
167
+ @patch("badfish.main.get_now", fixed_datetime)
168
168
  @patch("aiohttp.ClientSession.delete")
169
169
  @patch("aiohttp.ClientSession.post")
170
170
  @patch("aiohttp.ClientSession.get")
@@ -254,7 +254,7 @@ class TestImportSCP(TestBase):
254
254
  _, err = self.badfish_call()
255
255
  assert err == RESPONSE_EXPORT_SCP_NO_LOCATION
256
256
 
257
- @patch("src.badfish.main.get_now", fixed_datetime)
257
+ @patch("badfish.main.get_now", fixed_datetime)
258
258
  @patch("aiohttp.ClientSession.delete")
259
259
  @patch("aiohttp.ClientSession.post")
260
260
  @patch("aiohttp.ClientSession.get")
@@ -1,6 +0,0 @@
1
- [build-system]
2
- requires = ["setuptools>=39", "wheel"]
3
- build-backend = "setuptools.build_meta"
4
-
5
- [tool.black]
6
- line-length = 120
badfish-1.0.6/setup.py DELETED
@@ -1,5 +0,0 @@
1
- import setuptools
2
-
3
- setuptools.setup(
4
- version="1.0.6"
5
- )
File without changes
@@ -1,215 +0,0 @@
1
- # The MIT License
2
- #
3
- # Copyright (c) 2018 aio-libs team https://github.com/aio-libs/
4
- # Copyright (c) 2017 Ocean S. A. https://ocean.io/
5
- # Copyright (c) 2016-2017 WikiBusiness Corporation http://wikibusiness.org/
6
- #
7
- # Permission is hereby granted, free of charge, to any person obtaining a copy
8
- # of this software and associated documentation files (the "Software"), to deal
9
- # in the Software without restriction, including without limitation the rights
10
- # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
11
- # copies of the Software, and to permit persons to whom the Software is
12
- # furnished to do so, subject to the following conditions:
13
- #
14
- # The above copyright notice and this permission notice shall be included in
15
- # all copies or substantial portions of the Software.
16
- #
17
- # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18
- # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19
- # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
20
- # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21
- # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
22
- # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
23
- # THE SOFTWARE.
24
-
25
- import asyncio
26
- from collections import OrderedDict
27
- from functools import _CacheInfo, _make_key, partial, wraps
28
-
29
-
30
- __version__ = "1.0.2"
31
-
32
- __all__ = ("alru_cache",)
33
-
34
-
35
- def unpartial(fn):
36
- while hasattr(fn, "func"):
37
- fn = fn.func
38
-
39
- return fn
40
-
41
-
42
- def _done_callback(fut, task):
43
- if task.cancelled():
44
- fut.cancel()
45
- return
46
-
47
- exc = task.exception()
48
- if exc is not None:
49
- fut.set_exception(exc)
50
- return
51
-
52
- fut.set_result(task.result())
53
-
54
-
55
- def _cache_invalidate(wrapped, typed, *args, **kwargs):
56
- key = _make_key(args, kwargs, typed)
57
-
58
- exists = key in wrapped._cache
59
-
60
- if exists:
61
- wrapped._cache.pop(key)
62
-
63
- return exists
64
-
65
-
66
- def _cache_clear(wrapped):
67
- wrapped.hits = wrapped.misses = 0
68
- wrapped._cache = OrderedDict()
69
- wrapped.tasks = set()
70
-
71
-
72
- def _open(wrapped):
73
- if not wrapped.closed:
74
- raise RuntimeError("alru_cache is not closed")
75
-
76
- was_closed = wrapped.hits == wrapped.misses == len(wrapped.tasks) == len(wrapped._cache) == 0
77
-
78
- if not was_closed:
79
- raise RuntimeError("alru_cache was not closed correctly")
80
-
81
- wrapped.closed = False
82
-
83
-
84
- def _close(wrapped, *, cancel=False, return_exceptions=True):
85
- if wrapped.closed:
86
- raise RuntimeError("alru_cache is closed")
87
-
88
- wrapped.closed = True
89
-
90
- if cancel:
91
- for task in wrapped.tasks:
92
- if not task.done(): # not sure is it possible
93
- task.cancel()
94
-
95
- return _wait_closed(wrapped, return_exceptions=return_exceptions)
96
-
97
-
98
- async def _wait_closed(wrapped, *, return_exceptions):
99
- wait_closed = asyncio.gather(*wrapped.tasks, return_exceptions=return_exceptions)
100
-
101
- wait_closed.add_done_callback(partial(_close_waited, wrapped))
102
-
103
- ret = await wait_closed
104
-
105
- # hack to get _close_waited callback to be executed
106
- await asyncio.sleep(0)
107
-
108
- return ret
109
-
110
-
111
- def _close_waited(wrapped, _):
112
- wrapped.cache_clear()
113
-
114
-
115
- def _cache_info(wrapped, maxsize):
116
- return _CacheInfo(
117
- wrapped.hits,
118
- wrapped.misses,
119
- maxsize,
120
- len(wrapped._cache),
121
- )
122
-
123
-
124
- def __cache_touch(wrapped, key):
125
- try:
126
- wrapped._cache.move_to_end(key)
127
- except KeyError: # not sure is it possible
128
- pass
129
-
130
-
131
- def _cache_hit(wrapped, key):
132
- wrapped.hits += 1
133
- __cache_touch(wrapped, key)
134
-
135
-
136
- def _cache_miss(wrapped, key):
137
- wrapped.misses += 1
138
- __cache_touch(wrapped, key)
139
-
140
-
141
- def alru_cache(
142
- fn=None,
143
- maxsize=128,
144
- typed=False,
145
- *,
146
- cache_exceptions=True,
147
- ):
148
- def wrapper(fn):
149
- _origin = unpartial(fn)
150
-
151
- if not asyncio.iscoroutinefunction(_origin):
152
- raise RuntimeError("Coroutine function is required, got {}".format(fn))
153
-
154
- # functools.partialmethod support
155
- if hasattr(fn, "_make_unbound_method"):
156
- fn = fn._make_unbound_method()
157
-
158
- @wraps(fn)
159
- async def wrapped(*fn_args, **fn_kwargs):
160
- if wrapped.closed:
161
- raise RuntimeError("alru_cache is closed for {}".format(wrapped))
162
-
163
- loop = asyncio.get_event_loop()
164
-
165
- key = _make_key(fn_args, fn_kwargs, typed)
166
-
167
- fut = wrapped._cache.get(key)
168
-
169
- if fut is not None:
170
- if not fut.done():
171
- _cache_hit(wrapped, key)
172
- return await asyncio.shield(fut)
173
-
174
- exc = fut._exception
175
-
176
- if exc is None or cache_exceptions:
177
- _cache_hit(wrapped, key)
178
- return fut.result()
179
-
180
- # exception here and cache_exceptions == False
181
- wrapped._cache.pop(key)
182
-
183
- fut = loop.create_future()
184
- task = loop.create_task(fn(*fn_args, **fn_kwargs))
185
- task.add_done_callback(partial(_done_callback, fut))
186
-
187
- wrapped.tasks.add(task)
188
- task.add_done_callback(wrapped.tasks.remove)
189
-
190
- wrapped._cache[key] = fut
191
-
192
- if maxsize is not None and len(wrapped._cache) > maxsize:
193
- wrapped._cache.popitem(last=False)
194
-
195
- _cache_miss(wrapped, key)
196
- return await asyncio.shield(fut)
197
-
198
- _cache_clear(wrapped)
199
- wrapped._origin = _origin
200
- wrapped.closed = False
201
- wrapped.cache_info = partial(_cache_info, wrapped, maxsize)
202
- wrapped.cache_clear = partial(_cache_clear, wrapped)
203
- wrapped.invalidate = partial(_cache_invalidate, wrapped, typed)
204
- wrapped.close = partial(_close, wrapped)
205
- wrapped.open = partial(_open, wrapped)
206
-
207
- return wrapped
208
-
209
- if fn is None:
210
- return wrapper
211
-
212
- if callable(fn) or hasattr(fn, "_make_unbound_method"):
213
- return wrapper(fn)
214
-
215
- raise NotImplementedError("{} decorating is not supported".format(fn))
File without changes
File without changes
File without changes
File without changes
File without changes