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(),
|
mac_addr=self.agent.get_agent_mac_addr(),
|
||||||
url=self.agent.get_agent_url(),
|
url=self.agent.get_agent_url(),
|
||||||
version=self.agent.version,
|
version=self.agent.version,
|
||||||
mode=self.agent.mode_implementation.name)
|
mode=self.agent.get_mode_name())
|
||||||
self.error_delay = self.initial_delay
|
self.error_delay = self.initial_delay
|
||||||
self.log.info('heartbeat successful')
|
self.log.info('heartbeat successful')
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
@ -109,11 +109,11 @@ class TeethAgentHeartbeater(threading.Thread):
|
|||||||
|
|
||||||
|
|
||||||
class TeethAgent(object):
|
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.api_url = api_url
|
||||||
self.listen_address = listen_address
|
self.listen_address = listen_address
|
||||||
self.advertise_address = advertise_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.version = pkg_resources.get_distribution('teeth-agent').version
|
||||||
self.api = api.TeethAgentAPIServer(self)
|
self.api = api.TeethAgentAPIServer(self)
|
||||||
self.command_results = collections.OrderedDict()
|
self.command_results = collections.OrderedDict()
|
||||||
@ -123,10 +123,16 @@ class TeethAgent(object):
|
|||||||
self.log = structlog.get_logger()
|
self.log = structlog.get_logger()
|
||||||
self.started_at = None
|
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):
|
def get_status(self):
|
||||||
"""Retrieve a serializable status."""
|
"""Retrieve a serializable status."""
|
||||||
return TeethAgentStatus(
|
return TeethAgentStatus(
|
||||||
mode=self.mode_implementation.name,
|
mode=self.get_mode_name(),
|
||||||
started_at=self.started_at,
|
started_at=self.started_at,
|
||||||
version=self.version
|
version=self.version
|
||||||
)
|
)
|
||||||
@ -150,16 +156,38 @@ class TeethAgent(object):
|
|||||||
raise errors.RequestedObjectNotFoundError('Command Result',
|
raise errors.RequestedObjectNotFoundError('Command Result',
|
||||||
result_id)
|
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):
|
def execute_command(self, command_name, **kwargs):
|
||||||
"""Execute an agent command."""
|
"""Execute an agent command."""
|
||||||
with self.command_lock:
|
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:
|
if len(self.command_results) > 0:
|
||||||
last_command = self.command_results.values()[-1]
|
last_command = self.command_results.values()[-1]
|
||||||
if not last_command.is_done():
|
if not last_command.is_done():
|
||||||
raise errors.CommandExecutionError('agent is busy')
|
raise errors.CommandExecutionError('agent is busy')
|
||||||
|
|
||||||
try:
|
try:
|
||||||
result = self.mode_implementation.execute(command_name,
|
result = self.mode_implementation.execute(command_part,
|
||||||
**kwargs)
|
**kwargs)
|
||||||
except rest_errors.InvalidContentError as e:
|
except rest_errors.InvalidContentError as e:
|
||||||
# Any command may raise a InvalidContentError which will be
|
# Any command may raise a InvalidContentError which will be
|
||||||
@ -235,19 +263,6 @@ def build_agent(api_url,
|
|||||||
if not listen_host:
|
if not listen_host:
|
||||||
listen_host = advertise_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,
|
return TeethAgent(api_url,
|
||||||
(listen_host, listen_port),
|
(listen_host, listen_port),
|
||||||
(advertise_host, advertise_port),
|
(advertise_host, advertise_port))
|
||||||
mode_implementation)
|
|
||||||
|
@ -127,7 +127,8 @@ class BaseAgentMode(object):
|
|||||||
|
|
||||||
def execute(self, command_name, **kwargs):
|
def execute(self, command_name, **kwargs):
|
||||||
if command_name not in self.command_map:
|
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)
|
result = self.command_map[command_name](command_name, **kwargs)
|
||||||
|
|
||||||
|
@ -121,8 +121,7 @@ class TestBaseAgent(unittest.TestCase):
|
|||||||
indent=4)
|
indent=4)
|
||||||
self.agent = agent.TeethAgent('https://fake_api.example.org:8081/',
|
self.agent = agent.TeethAgent('https://fake_api.example.org:8081/',
|
||||||
('localhost', 9999),
|
('localhost', 9999),
|
||||||
('localhost', 9999),
|
('localhost', 9999))
|
||||||
FakeMode())
|
|
||||||
|
|
||||||
def assertEqualEncoded(self, a, b):
|
def assertEqualEncoded(self, a, b):
|
||||||
# Evidently JSONEncoder.default() can't handle None (??) so we have to
|
# Evidently JSONEncoder.default() can't handle None (??) so we have to
|
||||||
@ -144,10 +143,11 @@ class TestBaseAgent(unittest.TestCase):
|
|||||||
|
|
||||||
def test_execute_command(self):
|
def test_execute_command(self):
|
||||||
do_something_impl = mock.Mock()
|
do_something_impl = mock.Mock()
|
||||||
|
self.agent.mode_implementation = FakeMode()
|
||||||
command_map = self.agent.mode_implementation.command_map
|
command_map = self.agent.mode_implementation.command_map
|
||||||
command_map['do_something'] = do_something_impl
|
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')
|
do_something_impl.assert_called_once_with('do_something', foo='bar')
|
||||||
|
|
||||||
def test_execute_invalid_command(self):
|
def test_execute_invalid_command(self):
|
||||||
|
Loading…
x
Reference in New Issue
Block a user