Skip to content

Commit

Permalink
Merge branch 'master' into add-pod-property-to-machines
Browse files Browse the repository at this point in the history
  • Loading branch information
r00ta authored Jul 18, 2023
2 parents c964794 + fd95219 commit fea8c2c
Show file tree
Hide file tree
Showing 19 changed files with 89 additions and 51 deletions.
16 changes: 9 additions & 7 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -10,13 +10,13 @@ on:

jobs:
lint:
runs-on: ubuntu-latest
runs-on: ubuntu-20.04
steps:
- name: Repository checkout
uses: actions/checkout@v2
uses: actions/checkout@v3

- name: Set up Python
uses: actions/setup-python@v2
uses: actions/setup-python@v4
with:
python-version: "3.6"

Expand All @@ -29,19 +29,21 @@ jobs:
tox -e lint
test:
runs-on: ubuntu-latest
runs-on: ubuntu-20.04
strategy:
matrix:
python-version:
- "3.6"
- "3.7"
- "3.8"
- "3.9"
- "3.10"
steps:
- name: Repository checkout
uses: actions/checkout@v2
uses: actions/checkout@v3

- name: Set up Python
uses: actions/setup-python@v2
uses: actions/setup-python@v4
with:
python-version: ${{ matrix.python-version }}

Expand All @@ -51,5 +53,5 @@ jobs:
- name: Test
run: |
tox -e py
tox -e py3
codecov
2 changes: 1 addition & 1 deletion doc/client/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ Web API.
```python
#!/usr/bin/env python3.6

from maas.client import connect
import maas.client

# Replace … with an API key previously obtained by hand from
# http://$host:$port/MAAS/account/prefs/.
Expand Down
31 changes: 19 additions & 12 deletions maas/client/bones/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,9 @@
__all__ = ["CallError", "SessionAPI"]

import typing
from collections import Iterable, namedtuple

from collections import namedtuple
from collections.abc import Iterable
import json
from urllib.parse import urlparse

Expand Down Expand Up @@ -36,7 +38,7 @@ async def fromURL(cls, url, *, credentials=None, insecure=False):
# For now just re-raise as SessionError.
raise SessionError(str(error))
else:
session = cls(description, credentials)
session = cls(url, description, credentials)
session.scheme = urlparse(url).scheme
session.insecure = insecure
return session
Expand All @@ -47,7 +49,7 @@ def fromProfile(cls, profile):
:see: `ProfileStore`.
"""
session = cls(profile.description, profile.credentials)
session = cls(profile.url, profile.description, profile.credentials)
session.scheme = urlparse(profile.url).scheme
session.insecure = profile.other.get("insecure", False)
return session
Expand All @@ -73,7 +75,7 @@ async def login(cls, url, *, username=None, password=None, insecure=False):
profile = await helpers.login(
url=url, username=username, password=password, insecure=insecure
)
session = cls(profile.description, profile.credentials)
session = cls(url, profile.description, profile.credentials)
session.scheme = urlparse(url).scheme
session.insecure = insecure
return profile, session
Expand All @@ -88,7 +90,7 @@ async def connect(cls, url, *, apikey=None, insecure=False):
instance made using the profile.
"""
profile = await helpers.connect(url=url, apikey=apikey, insecure=insecure)
session = cls(profile.description, profile.credentials)
session = cls(url, profile.description, profile.credentials)
session.scheme = urlparse(url).scheme
session.insecure = insecure
return profile, session
Expand All @@ -98,13 +100,15 @@ async def connect(cls, url, *, apikey=None, insecure=False):
insecure = False
debug = False

def __init__(self, description, credentials=None):
def __init__(self, url, description, credentials=None):
"""Construct a `SessionAPI`.
:param url: MAAS URL
:param description: The description of the remote API. See `fromURL`.
:param credentials: Credentials for the remote system. Optional.
"""
super(SessionAPI, self).__init__()
self.__url = url
self.__description = description
self.__credentials = credentials
self.__populate()
Expand All @@ -114,15 +118,15 @@ def __populate(self):
if self.__credentials is None:
for resource in resources:
if resource["anon"] is not None:
handler = HandlerAPI(resource["anon"], resource, self)
handler = HandlerAPI(self.__url, resource["anon"], resource, self)
setattr(self, handler.name, handler)
else:
for resource in resources:
if resource["auth"] is not None:
handler = HandlerAPI(resource["auth"], resource, self)
handler = HandlerAPI(self.__url, resource["auth"], resource, self)
setattr(self, handler.name, handler)
elif resource["anon"] is not None:
handler = HandlerAPI(resource["anon"], resource, self)
handler = HandlerAPI(self.__url, resource["anon"], resource, self)
setattr(self, handler.name, handler)

@property
Expand Down Expand Up @@ -152,16 +156,18 @@ class HandlerAPI:
operations.
"""

def __init__(self, handler, resource, session):
def __init__(self, url, handler, resource, session):
"""Construct a `HandlerAPI`.
:param url: MAAS URL
:param handler: The handler description from the overall API
description document. See `SessionAPI`.
:param resource: The parent of `handler` in the API description
document. XXX: This does not appear to be needed.
:param session: The `SessionAPI`.
"""
super(HandlerAPI, self).__init__()
self.__url = url
self.__handler = handler
self.__resource = resource
self.__session = session
Expand All @@ -185,7 +191,8 @@ def uri(self):
This will typically contain replacement patterns; these are
interpolated in `CallAPI`.
"""
return self.__handler["uri"]
url = urlparse(self.__url)
return f"{url.scheme}://{url.netloc}{self.__handler['path']}"

@property
def params(self):
Expand Down Expand Up @@ -415,7 +422,7 @@ def prepare(self, data):

def expand(data):
for name, value in data.items():
if isinstance(value, Iterable):
if isinstance(value, Iterable) and not isinstance(value, str):
for value in value:
yield name, value
else:
Expand Down
12 changes: 8 additions & 4 deletions maas/client/bones/tests/test.py
Original file line number Diff line number Diff line change
Expand Up @@ -72,8 +72,10 @@ class TestActionAPI_APIVersions(TestCase):
for name, version, description in testing.api_descriptions
)

url = "http://127.0.0.1:8080/MAAS/api/2.0/"

def test__Version_read(self):
session = bones.SessionAPI(self.description)
session = bones.SessionAPI(self.url, self.description)
action = session.Version.read
self.assertThat(
action,
Expand All @@ -91,7 +93,7 @@ def test__Machines_deployment_status(self):
if self.version > (2, 0):
self.skipTest("Machines.deployment_status only in <= 2.0")

session = bones.SessionAPI(self.description, ("a", "b", "c"))
session = bones.SessionAPI(self.url, self.description, ("a", "b", "c"))
action = session.Machines.deployment_status
self.assertThat(
action,
Expand All @@ -106,7 +108,7 @@ def test__Machines_deployment_status(self):
)

def test__Machines_power_parameters(self):
session = bones.SessionAPI(self.description, ("a", "b", "c"))
session = bones.SessionAPI(self.url, self.description, ("a", "b", "c"))
action = session.Machines.power_parameters
self.assertThat(
action,
Expand All @@ -129,9 +131,11 @@ class TestCallAPI_APIVersions(TestCase):
for name, version, description in testing.api_descriptions
)

url = "http://127.0.0.1:8080/MAAS/api/2.0/"

def test__marshals_lists_into_query_as_repeat_parameters(self):
system_ids = list(str(uuid1()) for _ in range(3))
session = bones.SessionAPI(self.description, ("a", "b", "c"))
session = bones.SessionAPI(self.url, self.description, ("a", "b", "c"))
call = session.Machines.power_parameters.bind()
call.dispatch = Mock()

Expand Down
6 changes: 3 additions & 3 deletions maas/client/errors.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,15 +10,15 @@ def __init__(self, msg, obj):


class OperationNotAllowed(Exception):
""" MAAS says this operation cannot be performed. """
"""MAAS says this operation cannot be performed."""


class ObjectNotLoaded(Exception):
""" Object is not loaded. """
"""Object is not loaded."""


class CannotDelete(Exception):
""" Object cannot be deleted. """
"""Object cannot be deleted."""


class PowerError(MAASException):
Expand Down
13 changes: 4 additions & 9 deletions maas/client/flesh/tabular.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

from abc import ABCMeta, abstractmethod
import collections
from collections.abc import Iterable
import csv
import enum
from io import StringIO
Expand Down Expand Up @@ -297,9 +298,7 @@ def render(self, target, datum):
elif target is RenderTarget.json:
return datum
elif target is RenderTarget.csv:
if isinstance(datum, collections.Iterable) and not isinstance(
datum, (str, bytes)
):
if isinstance(datum, Iterable) and not isinstance(datum, (str, bytes)):
return ",".join(datum)
else:
return datum
Expand All @@ -308,9 +307,7 @@ def render(self, target, datum):
return ""
elif isinstance(datum, colorclass.Color):
return datum.value_no_colors
elif isinstance(datum, collections.Iterable) and not isinstance(
datum, (str, bytes)
):
elif isinstance(datum, Iterable) and not isinstance(datum, (str, bytes)):
return "\n".join(datum)
else:
return str(datum)
Expand All @@ -319,9 +316,7 @@ def render(self, target, datum):
return ""
elif isinstance(datum, colorclass.Color):
return datum
elif isinstance(datum, collections.Iterable) and not isinstance(
datum, (str, bytes)
):
elif isinstance(datum, Iterable) and not isinstance(datum, (str, bytes)):
return "\n".join(datum)
else:
return str(datum)
Expand Down
2 changes: 1 addition & 1 deletion maas/client/flesh/tests/test_controllers.py
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,7 @@ def test_returns_table_with_controllers(self):
cmd = controllers.cmd_controllers(parser)
subparser = controllers.cmd_controllers.register(parser)
options = subparser.parse_args([])
output = yaml.load(
output = yaml.safe_load(
cmd.execute(origin, options, target=tabular.RenderTarget.yaml)
)
self.assertEquals(
Expand Down
2 changes: 1 addition & 1 deletion maas/client/flesh/tests/test_devices.py
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ def test_returns_table_with_devices(self):
cmd = devices.cmd_devices(parser)
subparser = devices.cmd_devices.register(parser)
options = subparser.parse_args([])
output = yaml.load(
output = yaml.safe_load(
cmd.execute(origin, options, target=tabular.RenderTarget.yaml)
)
self.assertEquals(
Expand Down
2 changes: 1 addition & 1 deletion maas/client/flesh/tests/test_nodes.py
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ def test_returns_table_with_nodes(self):
cmd = nodes.cmd_nodes(parser)
subparser = nodes.cmd_nodes.register(parser)
options = subparser.parse_args([])
output = yaml.load(
output = yaml.safe_load(
cmd.execute(origin, options, target=tabular.RenderTarget.yaml)
)
self.assertEquals(
Expand Down
6 changes: 4 additions & 2 deletions maas/client/utils/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,9 @@
"vars_class",
]

from collections import Iterable, namedtuple

from collections import namedtuple
from collections.abc import Iterable
from functools import lru_cache, partial
from inspect import cleandoc, getdoc
from itertools import chain, cycle, repeat
Expand Down Expand Up @@ -175,7 +177,7 @@ def sign(uri, headers, credentials):
docstring = namedtuple("docstring", ("title", "body"))


@lru_cache(2 ** 10)
@lru_cache(2**10)
def parse_docstring(thing):
"""Parse a Python docstring, or the docstring found on `thing`.
Expand Down
2 changes: 1 addition & 1 deletion maas/client/utils/multipart.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@

__all__ = ["encode_multipart_data"]

from collections import Iterable, Mapping
from collections.abc import Iterable, Mapping
from email.generator import BytesGenerator
from email.mime.application import MIMEApplication
from email.mime.multipart import MIMEMultipart
Expand Down
3 changes: 2 additions & 1 deletion maas/client/viscera/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,8 @@
"OriginBase",
]

from collections import defaultdict, Iterable, Mapping, Sequence
from collections.abc import Iterable, Mapping, Sequence
from collections import defaultdict
from copy import copy
from datetime import datetime
from functools import wraps
Expand Down
2 changes: 1 addition & 1 deletion maas/client/viscera/boot_source_selections.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@

__all__ = ["BootSourceSelection", "BootSourceSelections"]

from collections import Sequence

from collections.abc import Sequence
from . import check, Object, ObjectField, ObjectFieldRelated, ObjectSet, ObjectType
from .boot_sources import BootSource

Expand Down
2 changes: 2 additions & 0 deletions maas/client/viscera/interfaces.py
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,8 @@ class Interface(Object, metaclass=InterfaceTypeMeta):
discovered = ObjectFieldRelatedSet(
"discovered", "InterfaceDiscoveredLinks", reverse=None
)
interface_speed = ObjectField.Checked("interface_speed", check(int), readonly=True)
link_speed = ObjectField.Checked("link_speed", check(int), readonly=True)

def __repr__(self):
return super(Interface, self).__repr__(fields={"name", "mac_address", "type"})
Expand Down
8 changes: 7 additions & 1 deletion maas/client/viscera/nodes.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,13 @@

__all__ = ["Node", "Nodes"]

from collections import Sequence
try:
# Python <= 3.9
from collections import Sequence
except:
# Python > 3.9
from collections.abc import Sequence

import typing

from . import (
Expand Down
6 changes: 6 additions & 0 deletions maas/client/viscera/sshkeys.py
Original file line number Diff line number Diff line change
Expand Up @@ -41,3 +41,9 @@ class SSHKey(Object, metaclass=SSHKeyType):
id = ObjectField.Checked("id", check(int), readonly=True)
key = ObjectField.Checked("key", check(str), readonly=True)
keysource = ObjectField.Checked("keysource", check_optional(str), readonly=True)

async def delete(self):
"""Delete this key."""
await self._handler.delete(
id=self.id,
)
Loading

0 comments on commit fea8c2c

Please sign in to comment.