mirror of
https://github.com/microsoft/onefuzz.git
synced 2025-06-16 20:08:09 +00:00
expose the ability manually override node reset (#201)
This commit is contained in:
@ -8,7 +8,13 @@ from typing import Optional, cast
|
|||||||
from uuid import UUID
|
from uuid import UUID
|
||||||
|
|
||||||
import azure.functions as func
|
import azure.functions as func
|
||||||
from onefuzztypes.enums import ErrorCode, NodeState, NodeTaskState, TaskState
|
from onefuzztypes.enums import (
|
||||||
|
ErrorCode,
|
||||||
|
NodeState,
|
||||||
|
NodeTaskState,
|
||||||
|
TaskDebugFlag,
|
||||||
|
TaskState,
|
||||||
|
)
|
||||||
from onefuzztypes.models import (
|
from onefuzztypes.models import (
|
||||||
Error,
|
Error,
|
||||||
NodeDoneEventData,
|
NodeDoneEventData,
|
||||||
@ -173,8 +179,21 @@ def on_worker_event(machine_id: UUID, event: WorkerEvent) -> None:
|
|||||||
],
|
],
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
if task.config.debug and (
|
||||||
|
TaskDebugFlag.keep_node_on_failure in task.config.debug
|
||||||
|
or TaskDebugFlag.keep_node_on_completion in task.config.debug
|
||||||
|
):
|
||||||
|
node.debug_keep_node = True
|
||||||
|
node.save()
|
||||||
|
|
||||||
else:
|
else:
|
||||||
task.mark_stopping()
|
task.mark_stopping()
|
||||||
|
if (
|
||||||
|
task.config.debug
|
||||||
|
and TaskDebugFlag.keep_node_on_completion in task.config.debug
|
||||||
|
):
|
||||||
|
node.debug_keep_node = True
|
||||||
|
node.save()
|
||||||
|
|
||||||
node.to_reimage(done=True)
|
node.to_reimage(done=True)
|
||||||
else:
|
else:
|
||||||
|
@ -6,7 +6,7 @@
|
|||||||
import azure.functions as func
|
import azure.functions as func
|
||||||
from onefuzztypes.enums import ErrorCode
|
from onefuzztypes.enums import ErrorCode
|
||||||
from onefuzztypes.models import Error
|
from onefuzztypes.models import Error
|
||||||
from onefuzztypes.requests import NodeGet, NodeSearch
|
from onefuzztypes.requests import NodeGet, NodeSearch, NodeUpdate
|
||||||
from onefuzztypes.responses import BoolResult
|
from onefuzztypes.responses import BoolResult
|
||||||
|
|
||||||
from ..onefuzzlib.pools import Node, NodeTasks
|
from ..onefuzzlib.pools import Node, NodeTasks
|
||||||
@ -42,6 +42,24 @@ def get(req: func.HttpRequest) -> func.HttpResponse:
|
|||||||
return ok(nodes)
|
return ok(nodes)
|
||||||
|
|
||||||
|
|
||||||
|
def post(req: func.HttpRequest) -> func.HttpResponse:
|
||||||
|
request = parse_request(NodeUpdate, req)
|
||||||
|
if isinstance(request, Error):
|
||||||
|
return not_ok(request, context="NodeUpdate")
|
||||||
|
|
||||||
|
node = Node.get_by_machine_id(request.machine_id)
|
||||||
|
if not node:
|
||||||
|
return not_ok(
|
||||||
|
Error(code=ErrorCode.UNABLE_TO_FIND, errors=["unable to find node"]),
|
||||||
|
context=request.machine_id,
|
||||||
|
)
|
||||||
|
if request.debug_keep_node is not None:
|
||||||
|
node.debug_keep_node = request.debug_keep_node
|
||||||
|
|
||||||
|
node.save()
|
||||||
|
return ok(BoolResult(result=True))
|
||||||
|
|
||||||
|
|
||||||
def delete(req: func.HttpRequest) -> func.HttpResponse:
|
def delete(req: func.HttpRequest) -> func.HttpResponse:
|
||||||
request = parse_request(NodeGet, req)
|
request = parse_request(NodeGet, req)
|
||||||
if isinstance(request, Error):
|
if isinstance(request, Error):
|
||||||
@ -55,6 +73,9 @@ def delete(req: func.HttpRequest) -> func.HttpResponse:
|
|||||||
)
|
)
|
||||||
|
|
||||||
node.set_halt()
|
node.set_halt()
|
||||||
|
if node.debug_keep_node:
|
||||||
|
node.debug_keep_node = False
|
||||||
|
node.save()
|
||||||
|
|
||||||
return ok(BoolResult(result=True))
|
return ok(BoolResult(result=True))
|
||||||
|
|
||||||
@ -72,6 +93,9 @@ def patch(req: func.HttpRequest) -> func.HttpResponse:
|
|||||||
)
|
)
|
||||||
|
|
||||||
node.stop()
|
node.stop()
|
||||||
|
if node.debug_keep_node:
|
||||||
|
node.debug_keep_node = False
|
||||||
|
node.save()
|
||||||
return ok(BoolResult(result=True))
|
return ok(BoolResult(result=True))
|
||||||
|
|
||||||
|
|
||||||
|
@ -847,7 +847,16 @@ class Scaleset(BASE_SCALESET, ORMMixin):
|
|||||||
logging.debug("scaleset delete will delete node: %s", self.scaleset_id)
|
logging.debug("scaleset delete will delete node: %s", self.scaleset_id)
|
||||||
return
|
return
|
||||||
|
|
||||||
machine_ids = [x.machine_id for x in nodes]
|
machine_ids = []
|
||||||
|
for node in nodes:
|
||||||
|
if node.debug_keep_node:
|
||||||
|
logging.warning(
|
||||||
|
"delete manually overridden %s:%s",
|
||||||
|
self.scaleset_id,
|
||||||
|
node.machine_id,
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
machine_ids.append(node.machine_id)
|
||||||
|
|
||||||
logging.info("deleting %s:%s", self.scaleset_id, machine_ids)
|
logging.info("deleting %s:%s", self.scaleset_id, machine_ids)
|
||||||
delete_vmss_nodes(self.scaleset_id, machine_ids)
|
delete_vmss_nodes(self.scaleset_id, machine_ids)
|
||||||
@ -865,7 +874,16 @@ class Scaleset(BASE_SCALESET, ORMMixin):
|
|||||||
logging.debug("scaleset delete will delete node: %s", self.scaleset_id)
|
logging.debug("scaleset delete will delete node: %s", self.scaleset_id)
|
||||||
return
|
return
|
||||||
|
|
||||||
machine_ids = [x.machine_id for x in nodes]
|
machine_ids = []
|
||||||
|
for node in nodes:
|
||||||
|
if node.debug_keep_node:
|
||||||
|
logging.warning(
|
||||||
|
"reimage manually overridden %s:%s",
|
||||||
|
self.scaleset_id,
|
||||||
|
node.machine_id,
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
machine_ids.append(node.machine_id)
|
||||||
|
|
||||||
result = reimage_vmss_nodes(self.scaleset_id, machine_ids)
|
result = reimage_vmss_nodes(self.scaleset_id, machine_ids)
|
||||||
if isinstance(result, Error):
|
if isinstance(result, Error):
|
||||||
|
@ -666,6 +666,7 @@ class Tasks(Endpoint):
|
|||||||
analyzer_env: Optional[Dict[str, str]] = None,
|
analyzer_env: Optional[Dict[str, str]] = None,
|
||||||
tags: Optional[Dict[str, str]] = None,
|
tags: Optional[Dict[str, str]] = None,
|
||||||
prereq_tasks: Optional[List[UUID]] = None,
|
prereq_tasks: Optional[List[UUID]] = None,
|
||||||
|
debug: Optional[List[enums.TaskDebugFlag]] = None,
|
||||||
) -> models.Task:
|
) -> models.Task:
|
||||||
""" Create a task """
|
""" Create a task """
|
||||||
self.logger.debug("creating task: %s", task_type)
|
self.logger.debug("creating task: %s", task_type)
|
||||||
@ -729,6 +730,7 @@ class Tasks(Endpoint):
|
|||||||
pool=models.TaskPool(count=vm_count, pool_name=pool_name),
|
pool=models.TaskPool(count=vm_count, pool_name=pool_name),
|
||||||
containers=containers_submit,
|
containers=containers_submit,
|
||||||
tags=tags,
|
tags=tags,
|
||||||
|
debug=debug,
|
||||||
)
|
)
|
||||||
|
|
||||||
return self.create_with_config(config)
|
return self.create_with_config(config)
|
||||||
@ -983,6 +985,28 @@ class Node(Endpoint):
|
|||||||
data=requests.NodeGet(machine_id=machine_id_expanded),
|
data=requests.NodeGet(machine_id=machine_id_expanded),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
def update(
|
||||||
|
self,
|
||||||
|
machine_id: UUID_EXPANSION,
|
||||||
|
*,
|
||||||
|
debug_keep_node: Optional[bool] = None,
|
||||||
|
) -> responses.BoolResult:
|
||||||
|
self.logger.debug("update node: %s", machine_id)
|
||||||
|
machine_id_expanded = self._disambiguate_uuid(
|
||||||
|
"machine_id",
|
||||||
|
machine_id,
|
||||||
|
lambda: [str(x.machine_id) for x in self.list()],
|
||||||
|
)
|
||||||
|
|
||||||
|
return self._req_model(
|
||||||
|
"POST",
|
||||||
|
responses.BoolResult,
|
||||||
|
data=requests.NodeUpdate(
|
||||||
|
machine_id=machine_id_expanded,
|
||||||
|
debug_keep_node=debug_keep_node,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
def list(
|
def list(
|
||||||
self,
|
self,
|
||||||
*,
|
*,
|
||||||
|
@ -86,6 +86,19 @@ class AsDict(argparse.Action):
|
|||||||
setattr(namespace, self.dest, as_dict)
|
setattr(namespace, self.dest, as_dict)
|
||||||
|
|
||||||
|
|
||||||
|
def arg_bool(arg: str) -> bool:
|
||||||
|
acceptable = ["true", "false"]
|
||||||
|
if arg not in acceptable:
|
||||||
|
raise argparse.ArgumentTypeError(
|
||||||
|
"invalid value: %s, must be %s"
|
||||||
|
% (
|
||||||
|
repr(arg),
|
||||||
|
" or ".join(acceptable),
|
||||||
|
)
|
||||||
|
)
|
||||||
|
return arg == "true"
|
||||||
|
|
||||||
|
|
||||||
def arg_dir(arg: str) -> str:
|
def arg_dir(arg: str) -> str:
|
||||||
if not os.path.isdir(arg):
|
if not os.path.isdir(arg):
|
||||||
raise argparse.ArgumentTypeError("not a directory: %s" % arg)
|
raise argparse.ArgumentTypeError("not a directory: %s" % arg)
|
||||||
@ -241,19 +254,27 @@ class Builder:
|
|||||||
}
|
}
|
||||||
return result
|
return result
|
||||||
|
|
||||||
if issubclass(annotation, bool) and default is False:
|
if issubclass(annotation, bool):
|
||||||
return {
|
if default is False:
|
||||||
"action": "store_true",
|
return {
|
||||||
"optional": True,
|
"action": "store_true",
|
||||||
"help": "(Default: False. Sets value to True)",
|
"optional": True,
|
||||||
}
|
"help": "(Default: False. Sets value to True)",
|
||||||
|
}
|
||||||
if issubclass(annotation, bool) and default is True:
|
elif default is True:
|
||||||
return {
|
return {
|
||||||
"action": "store_false",
|
"action": "store_false",
|
||||||
"optional": True,
|
"optional": True,
|
||||||
"help": "(Default: True. Sets value to False)",
|
"help": "(Default: True. Sets value to False)",
|
||||||
}
|
}
|
||||||
|
elif default is None:
|
||||||
|
return {
|
||||||
|
"type": arg_bool,
|
||||||
|
"optional": True,
|
||||||
|
"help": "Provide 'true' to set to true and 'false' to set to false",
|
||||||
|
}
|
||||||
|
else:
|
||||||
|
raise Exception("Argument parsing error: %s", repr(default))
|
||||||
|
|
||||||
if issubclass(annotation, BaseModel):
|
if issubclass(annotation, BaseModel):
|
||||||
|
|
||||||
|
@ -5,7 +5,7 @@
|
|||||||
|
|
||||||
from typing import Dict, List, Optional
|
from typing import Dict, List, Optional
|
||||||
|
|
||||||
from onefuzztypes.enums import OS, ContainerType, StatsFormat, TaskType
|
from onefuzztypes.enums import OS, ContainerType, StatsFormat, TaskDebugFlag, TaskType
|
||||||
from onefuzztypes.models import Job, NotificationConfig
|
from onefuzztypes.models import Job, NotificationConfig
|
||||||
from onefuzztypes.primitives import Container, Directory, File
|
from onefuzztypes.primitives import Container, Directory, File
|
||||||
|
|
||||||
@ -51,6 +51,7 @@ class AFL(Command):
|
|||||||
existing_inputs: Optional[Container] = None,
|
existing_inputs: Optional[Container] = None,
|
||||||
dryrun: bool = False,
|
dryrun: bool = False,
|
||||||
notification_config: Optional[NotificationConfig] = None,
|
notification_config: Optional[NotificationConfig] = None,
|
||||||
|
debug: Optional[List[TaskDebugFlag]] = None,
|
||||||
) -> Optional[Job]:
|
) -> Optional[Job]:
|
||||||
"""
|
"""
|
||||||
Basic AFL job
|
Basic AFL job
|
||||||
@ -144,6 +145,7 @@ class AFL(Command):
|
|||||||
stats_format=StatsFormat.AFL,
|
stats_format=StatsFormat.AFL,
|
||||||
task_wait_for_files=ContainerType.inputs,
|
task_wait_for_files=ContainerType.inputs,
|
||||||
tags=helper.tags,
|
tags=helper.tags,
|
||||||
|
debug=debug,
|
||||||
)
|
)
|
||||||
|
|
||||||
report_containers = [
|
report_containers = [
|
||||||
@ -170,6 +172,7 @@ class AFL(Command):
|
|||||||
check_debugger=True,
|
check_debugger=True,
|
||||||
tags=tags,
|
tags=tags,
|
||||||
prereq_tasks=[fuzzer_task.task_id],
|
prereq_tasks=[fuzzer_task.task_id],
|
||||||
|
debug=debug,
|
||||||
)
|
)
|
||||||
|
|
||||||
self.logger.info("done creating tasks")
|
self.logger.info("done creating tasks")
|
||||||
|
@ -5,7 +5,7 @@
|
|||||||
|
|
||||||
from typing import Dict, List, Optional
|
from typing import Dict, List, Optional
|
||||||
|
|
||||||
from onefuzztypes.enums import ContainerType, TaskType
|
from onefuzztypes.enums import ContainerType, TaskDebugFlag, TaskType
|
||||||
from onefuzztypes.models import Job, NotificationConfig
|
from onefuzztypes.models import Job, NotificationConfig
|
||||||
from onefuzztypes.primitives import Container, Directory, File
|
from onefuzztypes.primitives import Container, Directory, File
|
||||||
|
|
||||||
@ -46,6 +46,7 @@ class Libfuzzer(Command):
|
|||||||
tags: Optional[Dict[str, str]] = None,
|
tags: Optional[Dict[str, str]] = None,
|
||||||
check_retry_count: Optional[int] = None,
|
check_retry_count: Optional[int] = None,
|
||||||
crash_report_timeout: Optional[int] = None,
|
crash_report_timeout: Optional[int] = None,
|
||||||
|
debug: Optional[List[TaskDebugFlag]] = None,
|
||||||
) -> None:
|
) -> None:
|
||||||
|
|
||||||
fuzzer_containers = [
|
fuzzer_containers = [
|
||||||
@ -67,6 +68,7 @@ class Libfuzzer(Command):
|
|||||||
target_env=target_env,
|
target_env=target_env,
|
||||||
target_workers=target_workers,
|
target_workers=target_workers,
|
||||||
tags=tags,
|
tags=tags,
|
||||||
|
debug=debug,
|
||||||
)
|
)
|
||||||
|
|
||||||
coverage_containers = [
|
coverage_containers = [
|
||||||
@ -88,6 +90,7 @@ class Libfuzzer(Command):
|
|||||||
target_env=target_env,
|
target_env=target_env,
|
||||||
tags=tags,
|
tags=tags,
|
||||||
prereq_tasks=[fuzzer_task.task_id],
|
prereq_tasks=[fuzzer_task.task_id],
|
||||||
|
debug=debug,
|
||||||
)
|
)
|
||||||
|
|
||||||
report_containers = [
|
report_containers = [
|
||||||
@ -114,6 +117,7 @@ class Libfuzzer(Command):
|
|||||||
prereq_tasks=[fuzzer_task.task_id],
|
prereq_tasks=[fuzzer_task.task_id],
|
||||||
target_timeout=crash_report_timeout,
|
target_timeout=crash_report_timeout,
|
||||||
check_retry_count=check_retry_count,
|
check_retry_count=check_retry_count,
|
||||||
|
debug=debug,
|
||||||
)
|
)
|
||||||
|
|
||||||
def basic(
|
def basic(
|
||||||
@ -141,6 +145,7 @@ class Libfuzzer(Command):
|
|||||||
existing_inputs: Optional[Container] = None,
|
existing_inputs: Optional[Container] = None,
|
||||||
dryrun: bool = False,
|
dryrun: bool = False,
|
||||||
notification_config: Optional[NotificationConfig] = None,
|
notification_config: Optional[NotificationConfig] = None,
|
||||||
|
debug: Optional[List[TaskDebugFlag]] = None,
|
||||||
) -> Optional[Job]:
|
) -> Optional[Job]:
|
||||||
""" Basic libfuzzer job """
|
""" Basic libfuzzer job """
|
||||||
|
|
||||||
@ -207,6 +212,7 @@ class Libfuzzer(Command):
|
|||||||
tags=helper.tags,
|
tags=helper.tags,
|
||||||
crash_report_timeout=crash_report_timeout,
|
crash_report_timeout=crash_report_timeout,
|
||||||
check_retry_count=check_retry_count,
|
check_retry_count=check_retry_count,
|
||||||
|
debug=debug,
|
||||||
)
|
)
|
||||||
|
|
||||||
self.logger.info("done creating tasks")
|
self.logger.info("done creating tasks")
|
||||||
|
@ -9,7 +9,7 @@ import os
|
|||||||
import subprocess # nosec
|
import subprocess # nosec
|
||||||
from typing import Dict, List, Optional, Tuple
|
from typing import Dict, List, Optional, Tuple
|
||||||
|
|
||||||
from onefuzztypes.enums import OS, ContainerType
|
from onefuzztypes.enums import OS, ContainerType, TaskDebugFlag
|
||||||
from onefuzztypes.models import NotificationConfig
|
from onefuzztypes.models import NotificationConfig
|
||||||
from onefuzztypes.primitives import File
|
from onefuzztypes.primitives import File
|
||||||
|
|
||||||
@ -113,6 +113,7 @@ class OssFuzz(Command):
|
|||||||
max_target_count: int = 20,
|
max_target_count: int = 20,
|
||||||
sync_inputs: bool = False,
|
sync_inputs: bool = False,
|
||||||
notification_config: Optional[NotificationConfig] = None,
|
notification_config: Optional[NotificationConfig] = None,
|
||||||
|
debug: Optional[List[TaskDebugFlag]] = None,
|
||||||
) -> None:
|
) -> None:
|
||||||
""" OssFuzz style libfuzzer jobs """
|
""" OssFuzz style libfuzzer jobs """
|
||||||
|
|
||||||
@ -234,6 +235,7 @@ class OssFuzz(Command):
|
|||||||
target_options=target_options,
|
target_options=target_options,
|
||||||
target_env=target_env,
|
target_env=target_env,
|
||||||
tags=helper.tags,
|
tags=helper.tags,
|
||||||
|
debug=debug,
|
||||||
)
|
)
|
||||||
helpers.append(helper)
|
helpers.append(helper)
|
||||||
base_helper.wait()
|
base_helper.wait()
|
||||||
|
@ -5,7 +5,7 @@
|
|||||||
|
|
||||||
from typing import Dict, List, Optional
|
from typing import Dict, List, Optional
|
||||||
|
|
||||||
from onefuzztypes.enums import OS, ContainerType, TaskType
|
from onefuzztypes.enums import OS, ContainerType, TaskDebugFlag, TaskType
|
||||||
from onefuzztypes.models import Job, NotificationConfig
|
from onefuzztypes.models import Job, NotificationConfig
|
||||||
from onefuzztypes.primitives import Container, Directory, File
|
from onefuzztypes.primitives import Container, Directory, File
|
||||||
|
|
||||||
@ -47,6 +47,7 @@ class Radamsa(Command):
|
|||||||
disable_check_debugger: bool = False,
|
disable_check_debugger: bool = False,
|
||||||
dryrun: bool = False,
|
dryrun: bool = False,
|
||||||
notification_config: Optional[NotificationConfig] = None,
|
notification_config: Optional[NotificationConfig] = None,
|
||||||
|
debug: Optional[List[TaskDebugFlag]] = None,
|
||||||
) -> Optional[Job]:
|
) -> Optional[Job]:
|
||||||
""" Basic radamsa job """
|
""" Basic radamsa job """
|
||||||
|
|
||||||
@ -160,6 +161,7 @@ class Radamsa(Command):
|
|||||||
check_debugger=not disable_check_debugger,
|
check_debugger=not disable_check_debugger,
|
||||||
tags=helper.tags,
|
tags=helper.tags,
|
||||||
rename_output=rename_output,
|
rename_output=rename_output,
|
||||||
|
debug=debug,
|
||||||
)
|
)
|
||||||
|
|
||||||
report_containers = [
|
report_containers = [
|
||||||
@ -190,6 +192,7 @@ class Radamsa(Command):
|
|||||||
check_debugger=not disable_check_debugger,
|
check_debugger=not disable_check_debugger,
|
||||||
check_retry_count=check_retry_count,
|
check_retry_count=check_retry_count,
|
||||||
prereq_tasks=[fuzzer_task.task_id],
|
prereq_tasks=[fuzzer_task.task_id],
|
||||||
|
debug=debug,
|
||||||
)
|
)
|
||||||
|
|
||||||
if helper.platform == OS.windows:
|
if helper.platform == OS.windows:
|
||||||
@ -230,6 +233,7 @@ class Radamsa(Command):
|
|||||||
analyzer_env=analyzer_env,
|
analyzer_env=analyzer_env,
|
||||||
tags=helper.tags,
|
tags=helper.tags,
|
||||||
prereq_tasks=[fuzzer_task.task_id],
|
prereq_tasks=[fuzzer_task.task_id],
|
||||||
|
debug=debug,
|
||||||
)
|
)
|
||||||
|
|
||||||
self.logger.info("done creating tasks")
|
self.logger.info("done creating tasks")
|
||||||
|
@ -351,3 +351,8 @@ class GithubIssueState(Enum):
|
|||||||
class GithubIssueSearchMatch(Enum):
|
class GithubIssueSearchMatch(Enum):
|
||||||
title = "title"
|
title = "title"
|
||||||
body = "body"
|
body = "body"
|
||||||
|
|
||||||
|
|
||||||
|
class TaskDebugFlag(Enum):
|
||||||
|
keep_node_on_failure = "keep_node_on_failure"
|
||||||
|
keep_node_on_completion = "keep_node_on_completion"
|
||||||
|
@ -26,6 +26,7 @@ from .enums import (
|
|||||||
PoolState,
|
PoolState,
|
||||||
ScalesetState,
|
ScalesetState,
|
||||||
StatsFormat,
|
StatsFormat,
|
||||||
|
TaskDebugFlag,
|
||||||
TaskFeature,
|
TaskFeature,
|
||||||
TaskState,
|
TaskState,
|
||||||
TaskType,
|
TaskType,
|
||||||
@ -176,6 +177,7 @@ class TaskConfig(BaseModel):
|
|||||||
pool: Optional[TaskPool]
|
pool: Optional[TaskPool]
|
||||||
containers: List[TaskContainers]
|
containers: List[TaskContainers]
|
||||||
tags: Dict[str, str]
|
tags: Dict[str, str]
|
||||||
|
debug: Optional[List[TaskDebugFlag]]
|
||||||
|
|
||||||
|
|
||||||
class BlobRef(BaseModel):
|
class BlobRef(BaseModel):
|
||||||
@ -444,6 +446,7 @@ class Node(BaseModel):
|
|||||||
version: str = Field(default="1.0.0")
|
version: str = Field(default="1.0.0")
|
||||||
reimage_requested: bool = Field(default=False)
|
reimage_requested: bool = Field(default=False)
|
||||||
delete_requested: bool = Field(default=False)
|
delete_requested: bool = Field(default=False)
|
||||||
|
debug_keep_node: bool = Field(default=False)
|
||||||
|
|
||||||
|
|
||||||
class ScalesetSummary(BaseModel):
|
class ScalesetSummary(BaseModel):
|
||||||
|
@ -141,6 +141,11 @@ class NodeGet(BaseRequest):
|
|||||||
machine_id: UUID
|
machine_id: UUID
|
||||||
|
|
||||||
|
|
||||||
|
class NodeUpdate(BaseRequest):
|
||||||
|
machine_id: UUID
|
||||||
|
debug_keep_node: Optional[bool]
|
||||||
|
|
||||||
|
|
||||||
class ScalesetSearch(BaseRequest):
|
class ScalesetSearch(BaseRequest):
|
||||||
scaleset_id: Optional[UUID]
|
scaleset_id: Optional[UUID]
|
||||||
state: Optional[List[ScalesetState]]
|
state: Optional[List[ScalesetState]]
|
||||||
|
Reference in New Issue
Block a user