Skip to content

Commit

Permalink
Add the notion of a non-SUT node
Browse files Browse the repository at this point in the history
We might encounter environments/platforms in which helper nodes are
needed. For example, think of a test suite in which interaction with a
TFTP server is part of the game. It does not mean that that server is
one of the SUT ones. We want to be able to leverage the tooling around
nodes, nevertheless, for that one too, but skipping all the test flow
for it.

No support for that flag "requirement" nodes. If that is shown to make
sense in the future, we can add that.
  • Loading branch information
glima committed Apr 2, 2021
1 parent 493188f commit f0115de
Show file tree
Hide file tree
Showing 3 changed files with 54 additions and 15 deletions.
43 changes: 37 additions & 6 deletions lisa/node.py
Original file line number Diff line number Diff line change
Expand Up @@ -34,13 +34,16 @@ def __init__(
is_remote: bool = True,
is_default: bool = False,
logger_name: str = "node",
name: str = "",
sut: bool = True,
) -> None:
super().__init__()
self.is_default = is_default
self.is_remote = is_remote
self.capability = capability
self.name: str = ""
self.name = name
self.index = index
self.sut = sut

if self.is_remote:
self._shell: Optional[Shell] = None
Expand All @@ -64,6 +67,7 @@ def create(
node_type: str = constants.ENVIRONMENTS_NODES_REMOTE,
is_default: bool = False,
logger_name: str = "node",
sut: bool = True,
) -> Node:
if node_type == constants.ENVIRONMENTS_NODES_REMOTE:
is_remote = True
Expand All @@ -77,6 +81,7 @@ def create(
is_remote=is_remote,
is_default=is_default,
logger_name=logger_name,
sut=sut,
)
node.log.debug(f"created, type: '{node_type}', isDefault: {is_default}")
return node
Expand Down Expand Up @@ -196,14 +201,24 @@ def close(self) -> None:
self._shell.close()

def _initialize(self, *args: Any, **kwargs: Any) -> None:
# not a system under test, just a helper node, no init required
if not self.sut:
self.log.info(
"Skipping initialization of non-SUT node"
+ f" \"{self.name if self.name else 'unnamed'}\""
)
return
if self.is_remote:
assert (
self._connection_info
), "call setConnectionInfo before use remote node"
address = f"{self._connection_info.address}:{self._connection_info.port}"
else:
address = "localhost"
self.log.info(f"initializing node '{self.name}' {address}")
self.log.info(
f"initializing node \"{self.name if self.name else 'unnamed'}\""
+ f" at {address}"
)
self.shell.initialize()
self.os: OperatingSystem = OperatingSystem.create(self)

Expand Down Expand Up @@ -276,8 +291,11 @@ def default(self) -> Node:
self._default = default
return self._default

def list(self) -> Iterable[Node]:
# Hide non-SUT ones from this call. If needed, ask via name
def list(self, get_sut_only: bool = True) -> Iterable[Node]:
for node in self._list:
if get_sut_only and not node.sut:
continue
yield node

def __getitem__(self, key: Union[int, str]) -> Node:
Expand Down Expand Up @@ -312,6 +330,17 @@ def close(self) -> None:
for node in self._list:
node.close()

# Keep non-SUT node always at the end. We don't envision
# environments with a lot of those
def _insert(self, node: Node) -> None:
if not node.sut or (not self._list or self._list[-1].sut):
self._list.append(node)
else:
cur = len(self._list) - 1
while not self._list[cur].sut and cur > 0:
cur -= 1
self._list.insert(cur, node)

def from_local(
self,
node_runbook: schema.LocalNode,
Expand All @@ -325,9 +354,10 @@ def from_local(
capability=node_runbook.capability,
node_type=node_runbook.type,
is_default=node_runbook.is_default,
sut=node_runbook.sut,
logger_name=logger_name,
)
self._list.append(node)
self._insert(node)

return node

Expand All @@ -345,9 +375,10 @@ def from_remote(
capability=node_runbook.capability,
node_type=node_runbook.type,
is_default=node_runbook.is_default,
sut=node_runbook.sut,
logger_name=logger_name,
)
self._list.append(node)
self._insert(node)

fields = [
constants.ENVIRONMENTS_NODES_REMOTE_ADDRESS,
Expand Down Expand Up @@ -379,5 +410,5 @@ def from_requirement(self, node_requirement: schema.NodeSpace) -> Node:
node_type=constants.ENVIRONMENTS_NODES_REMOTE,
is_default=node_requirement.is_default,
)
self._list.append(node)
self._insert(node)
return node
3 changes: 2 additions & 1 deletion lisa/runners/lisa_runner.py
Original file line number Diff line number Diff line change
Expand Up @@ -91,7 +91,8 @@ def _run(self, id_: str) -> List[TestResult]:
if picked_result is None:
self._log.debug(
f"env[{environment.name}] skipped "
f"as not meet any case requirement"
f"as LISA has not met any case requirement match"
+ " against test cases"
)
continue

Expand Down
23 changes: 15 additions & 8 deletions lisa/schema.py
Original file line number Diff line number Diff line change
Expand Up @@ -575,31 +575,39 @@ def __post_init__(self, *args: Any, **kwargs: Any) -> None:

@dataclass_json()
@dataclass
class LocalNode(TypedSchema):
class Node(TypedSchema):
name: str = ""
is_default: bool = field(default=False)
capability: Capability = field(default_factory=Capability)
# All nodes considered Systems Under Test, unless otherwise
# explicitly stated. One might have such scenarios
# (e.g. dependency on one TFTP server, for a given
# platform/enviroment)
sut: bool = field(default=True)


@dataclass_json()
@dataclass
class LocalNode(Node):
type: str = field(
default=constants.ENVIRONMENTS_NODES_LOCAL,
metadata=metadata(
required=True,
validate=validate.OneOf([constants.ENVIRONMENTS_NODES_LOCAL]),
),
)
name: str = ""
is_default: bool = field(default=False)
capability: Capability = field(default_factory=Capability)


@dataclass_json()
@dataclass
class RemoteNode(TypedSchema):
class RemoteNode(Node):
type: str = field(
default=constants.ENVIRONMENTS_NODES_REMOTE,
metadata=metadata(
required=True,
validate=validate.OneOf([constants.ENVIRONMENTS_NODES_REMOTE]),
),
)
name: str = ""
is_default: bool = field(default=False)
address: str = ""
port: int = field(
default=22, metadata=metadata(validate=validate.Range(min=1, max=65535))
Expand All @@ -612,7 +620,6 @@ class RemoteNode(TypedSchema):
username: str = field(default="", metadata=metadata(required=True))
password: str = ""
private_key_file: str = ""
capability: Capability = field(default_factory=Capability)

def __post_init__(self, *args: Any, **kwargs: Any) -> None:
add_secret(self.address)
Expand Down

0 comments on commit f0115de

Please sign in to comment.