Skip to content
Closed
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
37 changes: 20 additions & 17 deletions juju/machine.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,16 @@

import ipaddress
import logging

import typing
import pyrfc3339

from . import model, tag, jasyncio
from juju.utils import juju_ssh_key_paths

from . import jasyncio, model, tag
from .annotationhelper import _get_annotations, _set_annotations
from .client import client
from .errors import JujuError
from juju.utils import juju_ssh_key_paths
from .status import InstanceStatusT, MachineAgentStatusT

log = logging.getLogger(__name__)

Expand All @@ -37,14 +39,14 @@ async def destroy(self, force=False):
return await self.model._wait('machine', self.id, 'remove')
remove = destroy

async def get_annotations(self):
async def get_annotations(self) -> typing.Dict[str, str]:
"""Get annotations on this machine.

:return dict: The annotations for this application
"""
return await _get_annotations(self.tag, self.connection)

async def set_annotations(self, annotations):
async def set_annotations(self, annotations: typing.Dict[str, str]):
"""Set annotations on this machine.

:param annotations map[string]string: the annotations as key/value
Expand All @@ -53,7 +55,7 @@ async def set_annotations(self, annotations):
"""
return await _set_annotations(self.tag, annotations, self.connection)

def _format_addr(self, addr):
def _format_addr(self, addr: str):
"""Validate and format IP address.

:param addr: IPv6 or IPv4 address
Expand All @@ -69,8 +71,8 @@ def _format_addr(self, addr):
fmt = '{}'
return fmt.format(ipaddr)

async def scp_to(self, source, destination, user='ubuntu', proxy=False,
scp_opts=''):
async def scp_to(self, source: str, destination: str, user: str = 'ubuntu', proxy: bool = False,
scp_opts: typing.Union[str, typing.List[str]] = ''):
"""Transfer files to this machine.

:param str source: Local path of file(s) to transfer
Expand All @@ -92,8 +94,8 @@ async def scp_to(self, source, destination, user='ubuntu', proxy=False,
destination = '{}@{}:{}'.format(user, address, destination)
await self._scp(source, destination, scp_opts)

async def scp_from(self, source, destination, user='ubuntu', proxy=False,
scp_opts=''):
async def scp_from(self, source: str, destination: str, user: str = 'ubuntu',
proxy: bool = False, scp_opts: typing.Union[str, typing.List[str]] = ''):
"""Transfer files from this machine.

:param str source: Remote path of file(s) to transfer
Expand All @@ -115,7 +117,7 @@ async def scp_from(self, source, destination, user='ubuntu', proxy=False,
source = '{}@{}:{}'.format(user, address, source)
await self._scp(source, destination, scp_opts)

async def _scp(self, source, destination, scp_opts):
async def _scp(self, source: str, destination: str, scp_opts: typing.Union[str, typing.List[str]]):
""" Execute an scp command. Requires a fully qualified source and
destination.
"""
Expand All @@ -135,7 +137,8 @@ async def _scp(self, source, destination, scp_opts):
raise JujuError("command failed: %s" % cmd)

async def ssh(
self, command, user='ubuntu', proxy=False, ssh_opts=None):
self, command: str, user: str = 'ubuntu', proxy: bool = False,
ssh_opts: typing.Optional[typing.Union[str, typing.List[str]]] = None):
"""Execute a command over SSH on this machine.

:param str command: Command to execute
Expand Down Expand Up @@ -168,7 +171,7 @@ async def ssh(
return stdout.decode()

@property
def agent_status(self):
def agent_status(self) -> MachineAgentStatusT:
"""Returns the current Juju agent status string.

"""
Expand All @@ -182,7 +185,7 @@ def agent_status_since(self):
return pyrfc3339.parse(self.safe_data['agent-status']['since'])

@property
def agent_version(self):
def agent_version(self) -> str:
"""Get the version of the Juju machine agent.

May return None if the agent is not yet available.
Expand All @@ -194,7 +197,7 @@ def agent_version(self):
return None

@property
def status(self):
def status(self) -> InstanceStatusT:
"""Returns the current machine provisioning status string.

"""
Expand All @@ -215,7 +218,7 @@ def status_since(self):
return pyrfc3339.parse(self.safe_data['instance-status']['since'])

@property
def dns_name(self):
def dns_name(self) -> typing.Optional[str]:
"""Get the DNS name for this machine. This is a best guess based on the
addresses available in current data.

Expand All @@ -236,7 +239,7 @@ def dns_name(self):
return None

@property
def hostname(self):
def hostname(self) -> typing.Optional[str]:
"""Get the hostname for this machine as reported by the machine agent
running on it. This is only supported on 2.8.10+ controllers.

Expand Down
191 changes: 191 additions & 0 deletions juju/status.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
# Licensed under the Apache V2, see LICENCE file for details.

import logging
from typing import Literal, Union

from .client import client

Expand All @@ -14,6 +15,196 @@

"""

# Status values common to machine and unit agents.
CommonStatusT = Literal['error', 'started']

# Error means the entity requires human intervention
# in order to operate correctly.
ERROR: CommonStatusT = 'error'

# Started is set when:
# The entity is actively participating in the model.
# For unit agents, this is a state we preserve for backwards
# compatibility with scripts during the life of Juju 1.x.
# In Juju 2.x, the agent-state will remain “active” and scripts
# will watch the unit-state instead for signals of application readiness.
STARTED: CommonStatusT = 'started'


# Status values specific to machine agents.
MachineAgentStatusT = Union[Literal['pending', 'stopped', 'down'], CommonStatusT]

# Pending is set when:
# The machine is not yet participating in the model.
PENDING: MachineAgentStatusT = 'pending'

# Stopped is set when:
# The machine's agent will perform no further action, other than
# to set the unit to Dead at a suitable moment.
STOPPED: MachineAgentStatusT = 'stopped'

# Down is set when:
# The machine ought to be signalling activity, but it cannot be
# detected.
DOWN: MachineAgentStatusT = 'down'


# Status values specific to unit agents.
UnitAgentStatusT = Union[
Literal['allocating', 'rebooting', 'executing', 'idle', 'failed', 'lost'],
CommonStatusT,
]

# Allocating is set when:
# The machine on which a unit is to be hosted is still being
# spun up in the cloud.
ALLOCATING: UnitAgentStatusT = 'allocating'

# Rebooting is set when:
# The machine on which this agent is running is being rebooted.
# The juju-agent should move from rebooting to idle when the reboot is complete.
REBOOTING: UnitAgentStatusT = 'rebooting'

# Executing is set when:
# The agent is running a hook or action. The human-readable message should reflect
# which hook or action is being run.
EXECUTING: UnitAgentStatusT = 'executing'

# Idle is set when:
# Once the agent is installed and running it will notify the Juju server and its state
# becomes 'idle'. It will stay 'idle' until some action (e.g. it needs to run a hook) or
# error (e.g it loses contact with the Juju server) moves it to a different state.
IDLE: UnitAgentStatusT = 'idle'

# Failed is set when:
# The unit agent has failed in some way,eg the agent ought to be signalling
# activity, but it cannot be detected. It might also be that the unit agent
# detected an unrecoverable condition and managed to tell the Juju server about it.
FAILED: UnitAgentStatusT = 'failed'

# Lost is set when:
# The juju agent has not communicated with the juju server for an unexpectedly long time;
# the unit agent ought to be signalling activity, but none has been detected.
LOST: UnitAgentStatusT = 'lost'

# Status values specific to applications and units, reflecting the
# state of the software itself.
AppOrUnitStatusT = Literal[
'unset', 'maintenance', 'terminated', 'unknown', 'waiting', 'blocked', 'active'
]
# Unset is only for applications, and is a placeholder status.
# The core/cache package deals with aggregating the unit status
# to the application level.
UNSET: AppOrUnitStatusT = 'unset'

# Maintenance is set when:
# The unit is not yet providing services, but is actively doing stuff
# in preparation for providing those services.
# This is a 'spinning' state, not an error state.
# It reflects activity on the unit itself, not on peers or related units.
MAINTENANCE: AppOrUnitStatusT = 'maintenance'

# Terminated is set when:
# This unit used to exist, we have a record of it (perhaps because of storage
# allocated for it that was flagged to survive it). Nonetheless, it is now gone.
TERMINATED: AppOrUnitStatusT = 'terminated'

# Unknown is set when:
# A unit-agent has finished calling install, config-changed, and start,
# but the charm has not called : AppOrUnitStatusT-set yet.
UNKNOWN: AppOrUnitStatusT = 'unknown'

# Waiting is set when:
# The unit is unable to progress to an active state because an application to
# which it is related is not running.
WAITING: AppOrUnitStatusT = 'waiting'

# Blocked is set when:
# The unit needs manual intervention to get back to the Running state.
BLOCKED: AppOrUnitStatusT = 'blocked'

# Active is set when:
# The unit believes it is correctly offering all the services it has
# been asked to offer.
ACTIVE: AppOrUnitStatusT = 'active'

# Status values specific to storage.
StorageStatusT = Literal['attaching', 'attached', 'detaching', 'detached']

# Attaching indicates that the storage is being attached
# to a machine.
ATTACHING: StorageStatusT = 'attaching'

# Attached indicates that the storage is attached to a
# machine.
ATTACHED: StorageStatusT = 'attached'

# Detaching indicates that the storage is being detached
# from a machine.
DETACHING: StorageStatusT = 'detaching'

# Detached indicates that the storage is not attached to
# any machine.
DETACHED: StorageStatusT = 'detached'

# Status values specific to models.
ModelStatusT = Literal['available', 'busy']

# Available indicates that the model is available for use.
AVAILABLE: ModelStatusT = 'available'

# Busy indicates that the model is not available for use because it is
# running a process that must take the model offline, such as a migration,
# upgrade, or backup. This is a spinning state, it is not an error state,
# and it should be expected that the model will eventually go back to
# available.
BUSY: ModelStatusT = 'busy'

# Status values specific to relations.
RelationStatusT = Literal['joining', 'joined', 'broken', 'suspending']

# Joining is used to signify that a relation should become joined soon.
JOINING: RelationStatusT = 'joining'

# Joined is the normal : RelationStatusT for a healthy, alive relation.
JOINED: RelationStatusT = 'joined'

# Broken is the : RelationStatusT for when a relation life goes to Dead.
BROKEN: RelationStatusT = 'broken'

# Suspending is used to signify that a relation will be temporarily broken
# pending action to resume it.
SUSPENDING: RelationStatusT = 'suspending'

# Suspended is used to signify that a relation is temporarily broken pending
# action to resume it.
SUSPENDED: RelationStatusT = 'suspended'

# Status values that are common to several entities.
CommonEntityStatusT = Literal['destroying']

# Destroying indicates that the entity is being destroyed.
# This is valid for volumes, filesystems, and models.
DESTROYING: CommonEntityStatusT = 'destroying'

# InstanceStatus
InstanceStatusT = Literal['', 'allocating', 'running', 'provisioning error']
EMPTY: InstanceStatusT = ''
PROVISIONING: InstanceStatusT = 'allocating'
RUNNING: InstanceStatusT = 'running'
PROVISIONING_ERROR: InstanceStatusT = 'provisioning error'

# ModificationStatus
ModificationStatusT = Literal['applied']
APPLIED: ModificationStatusT = 'applied'

# Messages
MESSAGE_WAIT_FOR_MACHINE = 'waiting for machine'
MESSAGE_WAIT_FOR_CONTAINER = 'waiting for container'
MESSAGE_INSTALLING_AGENT = 'installing agent'
MESSAGE_INITIALIZING_AGENT = 'agent initialising'
MESSAGE_INSTALLING_CHARM = 'installing charm software'


def derive_status(statues):
current = 'unknown'
Expand Down