Skip to content
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

refactor: [draft][rfc] mrack session + host as context holder #246

Draft
wants to merge 4 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
66 changes: 28 additions & 38 deletions src/mrack/dbdrivers/file.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,74 +15,64 @@
"""File database driver module."""

from os import path
from typing import Dict, Optional

from mrack.domain import domain_from_json
from mrack.host import host_from_json
from mrack.session import MrackSession
from mrack.utils import load_json, save_to_json

DOMAINS_KEY = "domains"
HOSTS_KEY = "hosts"


class FileDBDriver:
"""File database driver.

Serialize and load information into JSON file.
Serialize session's hosts and domains into JSON file.

Or loads it from JSON file into session.
"""

def __init__(self, file_path):
_session: MrackSession
_path: str
_raw_data: Optional[Dict]

def __init__(self, session: MrackSession, file_path: str):
"""Initialize DB driver."""
self._session = session
self._path = file_path
self._hosts = {}
self._raw_data = None
self.save_on_change = True
self.load()

def load(self):
"""Load configuration from file."""
self._hosts = {}
if not path.exists(self._path):
self._raw_data = {HOSTS_KEY: {}}
return self._hosts
self._raw_data = {
DOMAINS_KEY: [],
HOSTS_KEY: [],
}
return

self._raw_data = load_json(self._path)

raw_hosts = self._raw_data.get(HOSTS_KEY, [])
raw_domains = self._raw_data.get(DOMAINS_KEY, [])

self._hosts = {}
for raw_host in raw_hosts:
host = host_from_json(raw_host)
self._hosts[host.name] = host
host = host_from_json(self._session, raw_host)
self._session.hosts[host.name] = host

return self._hosts
for raw_domain in raw_domains:
domain = domain_from_json(self._session, raw_domain)
self._session.domains[domain.name] = domain

def save(self):
"""Save configuration to file."""
hosts = [host.to_json() for host in self._hosts.values()]
hosts = [host.to_json() for host in self._session.hosts.values()]
domains = [domain.to_json() for domain in self._session.domains.values()]
self._raw_data[HOSTS_KEY] = hosts
self._raw_data[DOMAINS_KEY] = domains
save_to_json(self._path, self._raw_data)

@property
def hosts(self):
"""Get all host objects loaded or to be saved."""
return self._hosts

def add_hosts(self, hosts):
"""Add a host object.

Save it to file automatically if `save_on_change` is set to True.
"""
for host in hosts:
self.hosts[host.name] = host

if self.save_on_change:
self.save()

def update_hosts(self, hosts):
"""Update managed host objects.

Only adds.
"""
self.add_hosts(hosts)

def delete_host(self, host):
"""Delete host object."""
if host.name in self.hosts:
del self.hosts[host.name]
105 changes: 105 additions & 0 deletions src/mrack/domain.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
# Copyright 2023 Red Hat Inc.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

"""Domain object."""

from typing import Dict, Set

from errors import MrackError

from mrack.host import Host
from mrack.session import MrackSession


class Domain:
"""Domain.

Domain as defined in job metadata file
"""

_session: MrackSession
_name: str
_type: str
_network: str
_hosts: Set[str]

def __init__(
self,
session,
name,
type,
network,
hosts: Set[str],
):
"""Initialize domain object."""
self._session = session
self._name = name
self._type = type
self._network = network
self._hosts = hosts

def __str__(self):
"""Return string representation of domain."""
host_count = len(self._hosts)
out = f"{self._name} {self._type} {self._network}, hosts: {host_count}"

return out

def to_json(self) -> Dict:
"""Transform object into representation which is acceptable by `json.dump`."""
return {
"name": self._name,
"type": self._type,
"network": self._network,
"hosts": self._hosts,
}

@property
def name(self) -> str:
"""Get host provisioning provider."""
return self._name

@property
def type(self) -> str:
"""Get host operating system."""
return self._type

@property
def network(self) -> str:
"""Get host group."""
return self._network

@property
def hosts(self) -> Set[Host]:
"""Get provider host id."""
hosts: Set[Host] = set()
for host in self._hosts:
try:
hosts.add(self._session.hosts[host])
except ValueError:
raise MrackError(f"Session is missing host: {host}")

return hosts


def domain_from_json(session: MrackSession, data: Dict) -> Domain:
"""Reverse method to Host.__json__() after json.loads()."""
domain = Domain(
session,
data["name"],
data["type"],
data["network"],
data["hosts"],
)
return domain
53 changes: 37 additions & 16 deletions src/mrack/host.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,11 @@

"""Host object."""

from typing import Dict, List, Optional

from mrack.providers import providers
from mrack.providers.provider import Provider
from mrack.session import MrackSession
from mrack.utils import object2json

STATUS_PENDING = "pending"
Expand All @@ -35,11 +39,12 @@
]


def host_from_json(host_data):
def host_from_json(session, host_data):
"""Reverse method to Host.__json__() after json.loads()."""
provider_name = host_data["provider"]
provider = providers.get(provider_name)
host = Host(
session,
provider,
host_data["host_id"],
host_data["name"],
Expand All @@ -62,8 +67,23 @@ class Host:
Normalized values from providers to offer consistent interface.
"""

_session: MrackSession
_provider: Provider
_host_id: str
_name: str
_operating_system: str
_group: str
_ip_addrs: List[str]
_status: str
_username: Optional[str]
_password: Optional[str]
_rawdata: Optional[Dict]
_error: Optional[Dict]
_meta_extra: Optional[Dict]

def __init__(
self,
session,
provider,
host_id,
name,
Expand All @@ -78,6 +98,7 @@ def __init__(
meta_extra=None,
):
"""Initialize host object."""
self._session = session
self._provider = provider
self._host_id = host_id
self._name = name
Expand All @@ -91,7 +112,7 @@ def __init__(
self._error = error_obj
self._meta_extra = meta_extra

def __str__(self):
def __str__(self) -> str:
"""Return string representation of host."""
net_str = " ".join(self._ip_addrs)

Expand All @@ -109,7 +130,7 @@ def __str__(self):
)
return out

def to_json(self):
def to_json(self) -> Dict:
"""Transform object into representation which is acceptable by `json.dump`."""
return {
"provider": self._provider.name,
Expand All @@ -127,47 +148,47 @@ def to_json(self):
}

@property
def provider(self):
def provider(self) -> Provider:
"""Get host provisioning provider."""
return self._provider

@property
def operating_system(self):
def operating_system(self) -> str:
"""Get host operating system."""
return self._operating_system

@property
def group(self):
def group(self) -> str:
"""Get host group."""
return self._group

@property
def host_id(self):
def host_id(self) -> str:
"""Get provider host id."""
return self._host_id

@property
def name(self):
def name(self) -> str:
"""Get host name."""
return self._name

@property
def ip_addrs(self):
def ip_addrs(self) -> List[str]:
"""Get host IP addresses."""
return self._ip_addrs

@property
def ip_addr(self):
def ip_addr(self) -> str:
"""Get first host IP address."""
return self._ip_addrs[0] if self._ip_addrs else ""

@property
def status(self):
def status(self) -> str:
"""Get host status."""
return self._status

@property
def error(self):
def error(self) -> Optional[Dict]:
"""Get host error object."""
return self._error

Expand All @@ -177,21 +198,21 @@ def error(self, value):
self._error = value

@property
def username(self):
def username(self) -> Optional[str]:
"""Get username for connecting to host."""
return self._username

@property
def password(self):
def password(self) -> Optional[str]:
"""Get password for connecting to host."""
return self._password

@property
def meta_extra(self):
def meta_extra(self) -> Optional[Dict]:
"""Get host extra meta information."""
return self._meta_extra

async def delete(self):
async def delete(self) -> bool:
"""Issue host deletion via associated provider."""
await self.provider.delete_host(self.host_id, self.name)
self._status = STATUS_DELETED
Expand Down
Loading