ready env: use default credential in platform

Previously, it must provide credential for each remote node. With this
change, remote node can use credential in platform. So if nodes use the
same credentials, it doesn't need to specify one by one.

Changes,
1. Remove `with_connection_info` in schema and RemoteNode. The
   conneciton info won't be validated when loading schema. It's delayed
   to `set_connection_info`.
2. Add `set_connection_info_by_runbook` with default values. It allows
   platform to fill default values.
3. Fill in connection information of remote nodes in platform. It can
   fill default value for remote nodes.
This commit is contained in:
Chi Song 2021-05-05 13:58:38 -07:00 коммит произвёл Gustavo Lima Chaves
Родитель 4e19421480
Коммит b1eac83c6a
3 изменённых файлов: 69 добавлений и 68 удалений

Просмотреть файл

@ -242,33 +242,6 @@ class Node(subclasses.BaseClassWithRunbookMixin, ContextMixin, InitializableMixi
class RemoteNode(Node):
def __init__(
self,
runbook: schema.RemoteNode,
index: int,
logger_name: str,
base_log_path: Optional[Path],
) -> None:
super().__init__(
index=index,
runbook=runbook,
logger_name=logger_name,
base_log_path=base_log_path,
)
if self._runbook.with_connection_info:
fields = [
constants.ENVIRONMENTS_NODES_REMOTE_ADDRESS,
constants.ENVIRONMENTS_NODES_REMOTE_PORT,
constants.ENVIRONMENTS_NODES_REMOTE_PUBLIC_ADDRESS,
constants.ENVIRONMENTS_NODES_REMOTE_PUBLIC_PORT,
constants.ENVIRONMENTS_NODES_REMOTE_USERNAME,
constants.ENVIRONMENTS_NODES_REMOTE_PASSWORD,
constants.ENVIRONMENTS_NODES_REMOTE_PRIVATE_KEY_FILE,
]
parameters = fields_to_dict(self._runbook, fields)
self.set_connection_info(**parameters)
def __repr__(self) -> str:
return str(self._connection_info)
@ -284,12 +257,42 @@ class RemoteNode(Node):
def type_schema(cls) -> Type[schema.TypedSchema]:
return schema.RemoteNode
def set_connection_info_by_runbook(
self,
default_username: str = "",
default_password: str = "",
default_private_key_file: str = "",
) -> None:
fields = [
constants.ENVIRONMENTS_NODES_REMOTE_ADDRESS,
constants.ENVIRONMENTS_NODES_REMOTE_PORT,
constants.ENVIRONMENTS_NODES_REMOTE_PUBLIC_ADDRESS,
constants.ENVIRONMENTS_NODES_REMOTE_PUBLIC_PORT,
]
parameters = fields_to_dict(self.runbook, fields)
# use default credential, if they are not specified
node_runbook = cast(schema.RemoteNode, self.runbook)
parameters[constants.ENVIRONMENTS_NODES_REMOTE_USERNAME] = (
node_runbook.username if node_runbook.username else default_username
)
parameters[constants.ENVIRONMENTS_NODES_REMOTE_PASSWORD] = (
node_runbook.password if node_runbook.password else default_password
)
parameters[constants.ENVIRONMENTS_NODES_REMOTE_PRIVATE_KEY_FILE] = (
node_runbook.private_key_file
if node_runbook.private_key_file
else default_private_key_file
)
self.set_connection_info(**parameters)
def set_connection_info(
self,
address: str = "",
port: int = 22,
port: Optional[int] = 22,
public_address: str = "",
public_port: int = 22,
public_port: Optional[int] = 22,
username: str = "root",
password: str = "",
private_key_file: str = "",
@ -299,6 +302,25 @@ class RemoteNode(Node):
"node is set connection information already, cannot set again"
)
if not address and not public_address:
raise LisaException(
"at least one of address and public_address need to be set"
)
elif not address:
address = public_address
elif not public_address:
public_address = address
if not port and not public_port:
raise LisaException("at least one of port and public_port need to be set")
elif not port:
port = public_port
elif not public_port:
public_port = port
assert public_port
assert port
self._connection_info = ConnectionInfo(
public_address,
public_port,
@ -307,6 +329,7 @@ class RemoteNode(Node):
private_key_file,
)
self._shell = SshShell(self._connection_info)
self.public_address = public_address
self.public_port = public_port
self.internal_address = address
@ -468,7 +491,6 @@ class Nodes:
type=constants.ENVIRONMENTS_NODES_REMOTE,
capability=min_requirement,
is_default=node_requirement.is_default,
with_connection_info=False,
)
node = Node.create(
index=len(self._list),

Просмотреть файл

@ -4,11 +4,12 @@
from __future__ import annotations
from functools import partial
from typing import Any, Dict, List, Type
from typing import Any, Dict, List, Type, cast
from lisa import schema
from lisa.environment import Environment, EnvironmentStatus
from lisa.feature import Feature, Features
from lisa.node import RemoteNode
from lisa.util import (
InitializableMixin,
LisaException,
@ -108,6 +109,19 @@ class Platform(subclasses.BaseClassWithRunbookMixin, InitializableMixin):
self.initialize()
log = get_logger(f"prepare[{environment.name}]", parent=self._log)
# check and fill connection information for RemoteNode. So that the
# RemoteNodes can share the same connection information with created
# nodes.
platform_runbook = cast(schema.Platform, self.runbook)
for node in environment.nodes.list():
if isinstance(node, RemoteNode):
node.set_connection_info_by_runbook(
default_username=platform_runbook.admin_username,
default_password=platform_runbook.admin_password,
default_private_key_file=platform_runbook.admin_private_key_file,
)
is_success = self._prepare_environment(environment, log)
if is_success:
environment.status = EnvironmentStatus.Prepared

Просмотреть файл

@ -2,7 +2,7 @@
# Licensed under the MIT license.
import copy
from dataclasses import InitVar, dataclass, field
from dataclasses import dataclass, field
from enum import Enum
from typing import Any, Callable, Dict, List, Optional, Type, TypeVar, Union, cast
@ -617,50 +617,15 @@ class RemoteNode(Node):
default=22,
metadata=metadata(validate=validate.Range(min=1, max=65535)),
)
username: str = field(default="", metadata=metadata(required=True))
username: str = ""
password: str = ""
private_key_file: str = ""
with_connection_info: InitVar[bool] = True
def __post_init__(self, *args: Any, **kwargs: Any) -> None:
if self.delay_parsed:
# the self needs to be overridden, since it's saved to delay parsed fields.
self.with_connection_info = self.delay_parsed.get(
"with_connection_info", True
)
else:
self.with_connection_info = True
if self.with_connection_info:
# if there is connection info, then validate it.
self.update_connection_info()
def update_connection_info(self) -> None:
add_secret(self.username, PATTERN_HEADTAIL)
add_secret(self.password)
add_secret(self.private_key_file)
if not self.address and not self.public_address:
raise LisaException(
"at least one of address and public_address need to be set"
)
elif not self.address:
self.address = self.public_address
elif not self.public_address:
self.public_address = self.address
if not self.port and not self.public_port:
raise LisaException("at least one of port and public_port need to be set")
elif not self.port:
self.port = self.public_port
elif not self.public_port:
self.public_port = self.port
if not self.password and not self.private_key_file:
raise LisaException(
"at least one of password or private_key_file need to be set in schema"
)
@dataclass_json()
@dataclass