Re-work req_channel in the master daemons, now we don't pass it down the stack, the master-side funcs need to return a dict of opts which specify which send_func to use

This commit is contained in:
Thomas Jackson 2015-01-27 08:12:56 -08:00
parent 8c66c4e857
commit 422d1c28a3
3 changed files with 53 additions and 27 deletions

View File

@ -654,8 +654,19 @@ class MWorker(multiprocessing.Process):
while True: while True:
try: try:
payload = self.req_channel.recv() payload = self.req_channel.recv()
ret = self._handle_payload(payload) ret, req_opts = self._handle_payload(payload)
# req_opts defines our response
# req_fun: default to send_clear
req_fun = req_opts.get('fun', 'send_clear')
if req_fun == 'send_clear':
self.req_channel.send_clear(ret)
elif req_fun == 'send':
self.req_channel.send(ret) self.req_channel.send(ret)
elif req_fun == 'send_private':
self.req_channel.send_private(ret, req_opts['key'], req_opts['tgt'])
else:
log.error('Unknown req_fun {0}'.format(req_fun))
# don't catch keyboard interrupts, just re-raise them # don't catch keyboard interrupts, just re-raise them
except KeyboardInterrupt: except KeyboardInterrupt:
raise raise
@ -719,7 +730,7 @@ class MWorker(multiprocessing.Process):
log.info('Clear payload received with command {cmd}'.format(**load)) log.info('Clear payload received with command {cmd}'.format(**load))
if load['cmd'].startswith('__'): if load['cmd'].startswith('__'):
return False return False
return getattr(self.clear_funcs, load['cmd'])(load) return getattr(self.clear_funcs, load['cmd'])(load), {'fun': 'send_clear'}
def _handle_aes(self, data): def _handle_aes(self, data):
''' '''
@ -746,7 +757,7 @@ class MWorker(multiprocessing.Process):
self.opts, self.opts,
self.key, self.key,
self.mkey) self.mkey)
self.aes_funcs = AESFuncs(self.opts, self.req_channel) self.aes_funcs = AESFuncs(self.opts)
self.__bind() self.__bind()
@ -757,7 +768,7 @@ class AESFuncs(object):
''' '''
# The AES Functions: # The AES Functions:
# #
def __init__(self, opts, req_channel): def __init__(self, opts):
''' '''
Create a new AESFuncs Create a new AESFuncs
@ -770,7 +781,6 @@ class AESFuncs(object):
self.opts = opts self.opts = opts
self.event = salt.utils.event.get_master_event(self.opts, self.opts['sock_dir']) self.event = salt.utils.event.get_master_event(self.opts, self.opts['sock_dir'])
self.serial = salt.payload.Serial(opts) self.serial = salt.payload.Serial(opts)
self.req_channel = req_channel
self.ckminions = salt.utils.minions.CkMinions(opts) self.ckminions = salt.utils.minions.CkMinions(opts)
# Make a client # Make a client
self.local = salt.client.get_local_client(self.opts['conf_file']) self.local = salt.client.get_local_client(self.opts['conf_file'])
@ -1344,7 +1354,7 @@ class AESFuncs(object):
# Don't honor private functions # Don't honor private functions
if func.startswith('__'): if func.startswith('__'):
# TODO: return some error? Seems odd to return {} # TODO: return some error? Seems odd to return {}
return self.req_channel.encrypt({}) return {}, {'fun': 'send'}
# Run the func # Run the func
if hasattr(self, func): if hasattr(self, func):
try: try:
@ -1368,18 +1378,18 @@ class AESFuncs(object):
func func
) )
) )
return self.req_channel.encrypt(False) return False, {'fun': 'send'}
# Don't encrypt the return value for the _return func # Don't encrypt the return value for the _return func
# (we don't care about the return value, so why encrypt it?) # (we don't care about the return value, so why encrypt it?)
if func == '_return': if func == '_return':
return ret return ret, {'fun': 'send'}
if func == '_pillar' and 'id' in load: if func == '_pillar' and 'id' in load:
if load.get('ver') != '2' and self.opts['pillar_version'] == 1: if load.get('ver') != '2' and self.opts['pillar_version'] == 1:
# Authorized to return old pillar proto # Authorized to return old pillar proto
return self.req_channel.encrypt(ret) return ret, {'fun': 'send'}
return self.req_channel.encrypt_private(ret, 'pillar', load['id']) return ret, {'fun': 'send_private', 'key': 'pillar', 'tgt': load['id']}
# Encrypt the return # Encrypt the return
return self.req_channel.encrypt(ret) return ret, {'fun': 'send'}
class ClearFuncs(object): class ClearFuncs(object):

View File

@ -71,18 +71,22 @@ class ReqServerChannel(object):
''' '''
raise NotImplementedError() raise NotImplementedError()
# TODO: use a send method? or have the recv return one? def send_clear(self, payload):
def send(self, load, tries=3, timeout=60):
''' '''
Send "load" to the master. Send a response to a recv()'d payload
''' '''
raise NotImplementedError() raise NotImplementedError()
# TODO: def send(self, payload):
def crypted_transfer_decode_dictentry(self, load, dictkey=None, tries=3, timeout=60):
''' '''
Send "load" to the master in a way that the load is only readable by Send a response to a recv()'d payload
the minion and the master (not other minions etc.) '''
raise NotImplementedError()
def send_private(self, payload, dictkey, target):
'''
Send a response to a recv()'d payload encrypted privately for target
''' '''
raise NotImplementedError() raise NotImplementedError()

View File

@ -345,13 +345,18 @@ class ZeroMQReqServerChannel(salt.transport.server.ReqServerChannel):
''' '''
if self.opts['aes'].value != self.crypticle.key_string: if self.opts['aes'].value != self.crypticle.key_string:
self.crypticle = salt.crypt.Crypticle(self.opts, self.opts['aes'].value) self.crypticle = salt.crypt.Crypticle(self.opts, self.opts['aes'].value)
return True
return False
def _decode_payload(self, payload): def _decode_payload(self, payload):
# we need to decrypt it # we need to decrypt it
if payload['enc'] == 'aes': if payload['enc'] == 'aes':
self._update_aes() # check if you need to update the aes key
try: try:
try:
payload['load'] = self.crypticle.loads(payload['load'])
except salt.crypt.AuthenticationError:
if not self._update_aes():
raise
payload['load'] = self.crypticle.loads(payload['load']) payload['load'] = self.crypticle.loads(payload['load'])
except Exception: except Exception:
# send something back to the client so the client so they know # send something back to the client so the client so they know
@ -390,19 +395,26 @@ class ZeroMQReqServerChannel(salt.transport.server.ReqServerChannel):
# TODO? maybe have recv() return this function, so this class isn't tied to # TODO? maybe have recv() return this function, so this class isn't tied to
# a send/recv order # a send/recv order
def send(self, payload): def send_clear(self, payload):
''' '''
Send a response to a recv()'d payload Send a response to a recv()'d payload
''' '''
self._socket.send(self.serial.dumps(payload)) self._socket.send(self.serial.dumps(payload))
def encrypt(self, payload): def send(self, payload):
''' '''
Regular encryption Send a response to a recv()'d payload
''' '''
return self.crypticle.dumps(payload) self.send_clear(self.crypticle.dumps(payload))
def encrypt_private(self, ret, dictkey, target):
def send_private(self, payload, dictkey, target):
'''
Send a response to a recv()'d payload encrypted privately for target
'''
self.send_clear(self._encrypt_private(payload, dictkey, target))
def _encrypt_private(self, ret, dictkey, target):
''' '''
The server equivalent of ReqChannel.crypted_transfer_decode_dictentry The server equivalent of ReqChannel.crypted_transfer_decode_dictentry
''' '''