cause the agent to lazily load a command mode
This commit is contained in:
parent
7523c9e430
commit
1bca58281b
@ -90,7 +90,7 @@ class TeethAgentHeartbeater(threading.Thread):
|
||||
mac_addr=self.agent.get_agent_mac_addr(),
|
||||
url=self.agent.get_agent_url(),
|
||||
version=self.agent.version,
|
||||
mode=self.agent.mode_implementation.name)
|
||||
mode=self.agent.get_mode_name())
|
||||
self.error_delay = self.initial_delay
|
||||
self.log.info('heartbeat successful')
|
||||
except Exception as e:
|
||||
@ -109,11 +109,11 @@ class TeethAgentHeartbeater(threading.Thread):
|
||||
|
||||
|
||||
class TeethAgent(object):
|
||||
def __init__(self, api_url, listen_address, advertise_address, mode_impl):
|
||||
def __init__(self, api_url, listen_address, advertise_address):
|
||||
self.api_url = api_url
|
||||
self.listen_address = listen_address
|
||||
self.advertise_address = advertise_address
|
||||
self.mode_implementation = mode_impl
|
||||
self.mode_implementation = None
|
||||
self.version = pkg_resources.get_distribution('teeth-agent').version
|
||||
self.api = api.TeethAgentAPIServer(self)
|
||||
self.command_results = collections.OrderedDict()
|
||||
@ -123,10 +123,16 @@ class TeethAgent(object):
|
||||
self.log = structlog.get_logger()
|
||||
self.started_at = None
|
||||
|
||||
def get_mode_name(self):
|
||||
if self.mode_implementation:
|
||||
return self.mode_implementation.name
|
||||
else:
|
||||
return 'NONE'
|
||||
|
||||
def get_status(self):
|
||||
"""Retrieve a serializable status."""
|
||||
return TeethAgentStatus(
|
||||
mode=self.mode_implementation.name,
|
||||
mode=self.get_mode_name(),
|
||||
started_at=self.started_at,
|
||||
version=self.version
|
||||
)
|
||||
@ -150,16 +156,38 @@ class TeethAgent(object):
|
||||
raise errors.RequestedObjectNotFoundError('Command Result',
|
||||
result_id)
|
||||
|
||||
def _split_command(self, command_name):
|
||||
command_parts = command_name.split('.', 1)
|
||||
if len(command_parts) != 2:
|
||||
raise errors.InvalidCommandError(
|
||||
'Command name must be of the form <mode>.<name>')
|
||||
|
||||
return (command_parts[0], command_parts[1])
|
||||
|
||||
def _verify_mode(self, mode_name, command_name):
|
||||
if not self.mode_implementation:
|
||||
try:
|
||||
self.mode_implementation = _load_mode_implementation(mode_name)
|
||||
except Exception:
|
||||
raise errors.InvalidCommandError(
|
||||
'Unknown mode: {}'.format(mode_name))
|
||||
elif self.get_mode_name().lower() != mode_name:
|
||||
raise errors.InvalidCommandError(
|
||||
'Agent is already in {} mode'.format(self.get_mode_name()))
|
||||
|
||||
def execute_command(self, command_name, **kwargs):
|
||||
"""Execute an agent command."""
|
||||
with self.command_lock:
|
||||
mode_part, command_part = self._split_command(command_name)
|
||||
self._verify_mode(mode_part, command_part)
|
||||
|
||||
if len(self.command_results) > 0:
|
||||
last_command = self.command_results.values()[-1]
|
||||
if not last_command.is_done():
|
||||
raise errors.CommandExecutionError('agent is busy')
|
||||
|
||||
try:
|
||||
result = self.mode_implementation.execute(command_name,
|
||||
result = self.mode_implementation.execute(command_part,
|
||||
**kwargs)
|
||||
except rest_errors.InvalidContentError as e:
|
||||
# Any command may raise a InvalidContentError which will be
|
||||
@ -235,19 +263,6 @@ def build_agent(api_url,
|
||||
if not listen_host:
|
||||
listen_host = advertise_host
|
||||
|
||||
mac_addr = hardware.get_manager().get_primary_mac_address()
|
||||
api_client = overlord_agent_api.APIClient(api_url)
|
||||
|
||||
log.info('fetching agent configuration from API',
|
||||
api_url=api_url,
|
||||
mac_addr=mac_addr)
|
||||
config = api_client.get_configuration(mac_addr)
|
||||
mode_name = config['mode']
|
||||
|
||||
log.info('loading mode implementation', mode=mode_name)
|
||||
mode_implementation = _load_mode_implementation(mode_name)
|
||||
|
||||
return TeethAgent(api_url,
|
||||
(listen_host, listen_port),
|
||||
(advertise_host, advertise_port),
|
||||
mode_implementation)
|
||||
(advertise_host, advertise_port))
|
||||
|
@ -127,7 +127,8 @@ class BaseAgentMode(object):
|
||||
|
||||
def execute(self, command_name, **kwargs):
|
||||
if command_name not in self.command_map:
|
||||
raise errors.InvalidCommandError(command_name)
|
||||
raise errors.InvalidCommandError(
|
||||
'Unknown command: {}'.format(command_name))
|
||||
|
||||
result = self.command_map[command_name](command_name, **kwargs)
|
||||
|
||||
|
@ -121,8 +121,7 @@ class TestBaseAgent(unittest.TestCase):
|
||||
indent=4)
|
||||
self.agent = agent.TeethAgent('https://fake_api.example.org:8081/',
|
||||
('localhost', 9999),
|
||||
('localhost', 9999),
|
||||
FakeMode())
|
||||
('localhost', 9999))
|
||||
|
||||
def assertEqualEncoded(self, a, b):
|
||||
# Evidently JSONEncoder.default() can't handle None (??) so we have to
|
||||
@ -144,10 +143,11 @@ class TestBaseAgent(unittest.TestCase):
|
||||
|
||||
def test_execute_command(self):
|
||||
do_something_impl = mock.Mock()
|
||||
self.agent.mode_implementation = FakeMode()
|
||||
command_map = self.agent.mode_implementation.command_map
|
||||
command_map['do_something'] = do_something_impl
|
||||
|
||||
self.agent.execute_command('do_something', foo='bar')
|
||||
self.agent.execute_command('fake.do_something', foo='bar')
|
||||
do_something_impl.assert_called_once_with('do_something', foo='bar')
|
||||
|
||||
def test_execute_invalid_command(self):
|
||||
|
Loading…
Reference in New Issue
Block a user