Fixed backend script name colision
This commit is contained in:
parent
39bf68caad
commit
2c122935b3
|
@ -98,8 +98,8 @@ class Bind9MasterDomainBackend(ServiceController):
|
||||||
from orchestra.contrib.orchestration.manager import router
|
from orchestra.contrib.orchestration.manager import router
|
||||||
operation = Operation(backend, domain, Operation.SAVE)
|
operation = Operation(backend, domain, Operation.SAVE)
|
||||||
servers = []
|
servers = []
|
||||||
for server in router.get_servers(operation):
|
for routes in router.get_routes(operation):
|
||||||
servers.append(server.get_ip())
|
servers.append(route.host.get_ip())
|
||||||
return servers
|
return servers
|
||||||
|
|
||||||
def get_masters_ips(self, domain):
|
def get_masters_ips(self, domain):
|
||||||
|
|
|
@ -51,14 +51,17 @@ def validate_forward(value):
|
||||||
|
|
||||||
def validate_sieve(value):
|
def validate_sieve(value):
|
||||||
sieve_name = '%s.sieve' % hashlib.md5(value.encode('utf8')).hexdigest()
|
sieve_name = '%s.sieve' % hashlib.md5(value.encode('utf8')).hexdigest()
|
||||||
path = os.path.join(settings.MAILBOXES_SIEVETEST_PATH, sieve_name)
|
test_path = os.path.join(settings.MAILBOXES_SIEVETEST_PATH, sieve_name)
|
||||||
with open(path, 'w') as f:
|
with open(test_path, 'w') as f:
|
||||||
f.write(value)
|
f.write(value)
|
||||||
context = {
|
context = {
|
||||||
'orchestra_root': paths.get_orchestra_dir()
|
'orchestra_root': paths.get_orchestra_dir()
|
||||||
}
|
}
|
||||||
sievetest = settings.MAILBOXES_SIEVETEST_BIN_PATH % context
|
sievetest = settings.MAILBOXES_SIEVETEST_BIN_PATH % context
|
||||||
test = run(' '.join([sievetest, path, '/dev/null']), silent=True)
|
try:
|
||||||
|
test = run(' '.join([sievetest, test_path, '/dev/null']), silent=True)
|
||||||
|
finally:
|
||||||
|
os.unlink(test_path)
|
||||||
if test.return_code:
|
if test.return_code:
|
||||||
errors = []
|
errors = []
|
||||||
for line in test.stderr.decode('utf8').splitlines():
|
for line in test.stderr.decode('utf8').splitlines():
|
||||||
|
|
|
@ -21,13 +21,13 @@ class Operation():
|
||||||
""" set() """
|
""" set() """
|
||||||
return hash(self) == hash(operation)
|
return hash(self) == hash(operation)
|
||||||
|
|
||||||
def __init__(self, backend, instance, action, servers=None):
|
def __init__(self, backend, instance, action, routes=None):
|
||||||
self.backend = backend
|
self.backend = backend
|
||||||
# instance should maintain any dynamic attribute until backend execution
|
# instance should maintain any dynamic attribute until backend execution
|
||||||
# deep copy is prefered over copy otherwise objects will share same atributes (queryset cache)
|
# deep copy is prefered over copy otherwise objects will share same atributes (queryset cache)
|
||||||
self.instance = copy.deepcopy(instance)
|
self.instance = copy.deepcopy(instance)
|
||||||
self.action = action
|
self.action = action
|
||||||
self.servers = servers
|
self.routes = routes
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def execute(cls, operations, async=False):
|
def execute(cls, operations, async=False):
|
||||||
|
|
|
@ -4,7 +4,7 @@ from django.apps import apps
|
||||||
from orchestra.contrib.orchestration import manager, Operation
|
from orchestra.contrib.orchestration import manager, Operation
|
||||||
from orchestra.contrib.orchestration.models import Server
|
from orchestra.contrib.orchestration.models import Server
|
||||||
from orchestra.contrib.orchestration.backends import ServiceBackend
|
from orchestra.contrib.orchestration.backends import ServiceBackend
|
||||||
from orchestra.utils.python import import_class, OrderedSet
|
from orchestra.utils.python import import_class, OrderedSet, AttrDict
|
||||||
|
|
||||||
|
|
||||||
class Command(BaseCommand):
|
class Command(BaseCommand):
|
||||||
|
@ -53,7 +53,7 @@ class Command(BaseCommand):
|
||||||
if servers:
|
if servers:
|
||||||
servers = servers.split(',')
|
servers = servers.split(',')
|
||||||
backends = backends.split(',')
|
backends = backends.split(',')
|
||||||
server_objects = []
|
routes = []
|
||||||
# Get and create missing Servers
|
# Get and create missing Servers
|
||||||
for server in servers:
|
for server in servers:
|
||||||
try:
|
try:
|
||||||
|
@ -62,12 +62,12 @@ class Command(BaseCommand):
|
||||||
server = Server(name=server, address=server)
|
server = Server(name=server, address=server)
|
||||||
server.full_clean()
|
server.full_clean()
|
||||||
server.save()
|
server.save()
|
||||||
server_objects.append(server)
|
routes.append(AttrDict(host=server, async=False))
|
||||||
# Generate operations for the given backend
|
# Generate operations for the given backend
|
||||||
for instance in queryset:
|
for instance in queryset:
|
||||||
for backend in backends:
|
for backend in backends:
|
||||||
backend = import_class(backend)
|
backend = import_class(backend)
|
||||||
operations.add(Operation(backend, instance, action, servers=server_objects))
|
operations.add(Operation(backend, instance, action, routes=routes))
|
||||||
else:
|
else:
|
||||||
for instance in queryset:
|
for instance in queryset:
|
||||||
manager.collect(instance, action, operations=operations, route_cache=route_cache)
|
manager.collect(instance, action, operations=operations, route_cache=route_cache)
|
||||||
|
@ -75,9 +75,9 @@ class Command(BaseCommand):
|
||||||
servers = []
|
servers = []
|
||||||
# Print scripts
|
# Print scripts
|
||||||
for key, value in scripts.items():
|
for key, value in scripts.items():
|
||||||
server, __ = key
|
route, __ = key
|
||||||
backend, operations = value
|
backend, operations = value
|
||||||
servers.append(server.name)
|
servers.append(str(route.host))
|
||||||
self.stdout.write('# Execute on %s' % server.name)
|
self.stdout.write('# Execute on %s' % server.name)
|
||||||
for method, commands in backend.scripts:
|
for method, commands in backend.scripts:
|
||||||
script = '\n'.join(commands)
|
script = '\n'.join(commands)
|
||||||
|
|
|
@ -23,7 +23,8 @@ router = import_class(settings.ORCHESTRATION_ROUTER)
|
||||||
def as_task(execute, log, operations):
|
def as_task(execute, log, operations):
|
||||||
def wrapper(*args, **kwargs):
|
def wrapper(*args, **kwargs):
|
||||||
""" send report """
|
""" send report """
|
||||||
# Tasks run on a separate transaction pool (thread), no need to temper with the transaction
|
# Remember that threads have their oun connection poll
|
||||||
|
# No need to EVER temper with the transaction here
|
||||||
try:
|
try:
|
||||||
log = execute(*args, **kwargs)
|
log = execute(*args, **kwargs)
|
||||||
if log.state != log.SUCCESS:
|
if log.state != log.SUCCESS:
|
||||||
|
@ -39,7 +40,7 @@ def as_task(execute, log, operations):
|
||||||
log.save(update_fields=('state', 'stderr'))
|
log.save(update_fields=('state', 'stderr'))
|
||||||
# We don't propagate the exception further to avoid transaction rollback
|
# We don't propagate the exception further to avoid transaction rollback
|
||||||
finally:
|
finally:
|
||||||
# Store the operation
|
# Store and log the operation
|
||||||
for operation in operations:
|
for operation in operations:
|
||||||
logger.info("Executed %s" % str(operation))
|
logger.info("Executed %s" % str(operation))
|
||||||
if operation.instance.pk:
|
if operation.instance.pk:
|
||||||
|
@ -56,13 +57,13 @@ def generate(operations):
|
||||||
scripts = OrderedDict()
|
scripts = OrderedDict()
|
||||||
cache = {}
|
cache = {}
|
||||||
block = False
|
block = False
|
||||||
# Generate scripts per server+backend
|
# Generate scripts per route+backend
|
||||||
for operation in operations:
|
for operation in operations:
|
||||||
logger.debug("Queued %s" % str(operation))
|
logger.debug("Queued %s" % str(operation))
|
||||||
if operation.servers is None:
|
if operation.routes is None:
|
||||||
operation.servers = router.get_servers(operation, cache=cache)
|
operation.routes = router.get_routes(operation, cache=cache)
|
||||||
for server in operation.servers:
|
for route in operation.routes:
|
||||||
key = (server, operation.backend)
|
key = (route, operation.backend)
|
||||||
if key not in scripts:
|
if key not in scripts:
|
||||||
backend, operations = (operation.backend(), [operation])
|
backend, operations = (operation.backend(), [operation])
|
||||||
scripts[key] = (backend, operations)
|
scripts[key] = (backend, operations)
|
||||||
|
@ -106,16 +107,16 @@ def execute(scripts, block=False, async=False):
|
||||||
threads_to_join = []
|
threads_to_join = []
|
||||||
logs = []
|
logs = []
|
||||||
for key, value in scripts.items():
|
for key, value in scripts.items():
|
||||||
server, __ = key
|
route, __ = key
|
||||||
backend, operations = value
|
backend, operations = value
|
||||||
args = (server,)
|
args = (route.host,)
|
||||||
kwargs = {
|
kwargs = {
|
||||||
'async': async or server.async
|
'async': async or route.async
|
||||||
}
|
}
|
||||||
log = backend.create_log(*args, **kwargs)
|
log = backend.create_log(*args, **kwargs)
|
||||||
kwargs['log'] = log
|
kwargs['log'] = log
|
||||||
task = as_task(backend.execute, log, operations)
|
task = as_task(backend.execute, log, operations)
|
||||||
logger.debug('%s is going to be executed on %s' % (backend, server))
|
logger.debug('%s is going to be executed on %s' % (backend, route.host))
|
||||||
if block:
|
if block:
|
||||||
# Execute one backend at a time, no need for threads
|
# Execute one backend at a time, no need for threads
|
||||||
task(*args, **kwargs)
|
task(*args, **kwargs)
|
||||||
|
@ -123,7 +124,7 @@ def execute(scripts, block=False, async=False):
|
||||||
task = close_connection(task)
|
task = close_connection(task)
|
||||||
thread = threading.Thread(target=task, args=args, kwargs=kwargs)
|
thread = threading.Thread(target=task, args=args, kwargs=kwargs)
|
||||||
thread.start()
|
thread.start()
|
||||||
if not server.async:
|
if not route.async:
|
||||||
threads_to_join.append(thread)
|
threads_to_join.append(thread)
|
||||||
logs.append(log)
|
logs.append(log)
|
||||||
[ thread.join() for thread in threads_to_join ]
|
[ thread.join() for thread in threads_to_join ]
|
||||||
|
@ -182,10 +183,10 @@ def collect(instance, action, **kwargs):
|
||||||
if not execute:
|
if not execute:
|
||||||
continue
|
continue
|
||||||
operation = Operation(backend_cls, selected, iaction)
|
operation = Operation(backend_cls, selected, iaction)
|
||||||
# Only schedule operations if the router gives servers to execute into
|
# Only schedule operations if the router has execution routes
|
||||||
servers = router.get_servers(operation, cache=route_cache)
|
routes = router.get_routes(operation, cache=route_cache)
|
||||||
if servers:
|
if routes:
|
||||||
operation.servers = servers
|
operation.routes = routes
|
||||||
if iaction != Operation.DELETE:
|
if iaction != Operation.DELETE:
|
||||||
# usually we expect to be using last object state,
|
# usually we expect to be using last object state,
|
||||||
# except when we are deleting it
|
# except when we are deleting it
|
||||||
|
|
|
@ -33,6 +33,8 @@ def SSH(backend, log, server, cmds, async=False):
|
||||||
digest = hashlib.md5(bscript).hexdigest()
|
digest = hashlib.md5(bscript).hexdigest()
|
||||||
path = os.path.join(settings.ORCHESTRATION_TEMP_SCRIPT_DIR, digest)
|
path = os.path.join(settings.ORCHESTRATION_TEMP_SCRIPT_DIR, digest)
|
||||||
remote_path = "%s.remote" % path
|
remote_path = "%s.remote" % path
|
||||||
|
# Ensure unique local paths for each file because of problems when os.remove(path)
|
||||||
|
path += '@%s' % str(server)
|
||||||
log.state = log.STARTED
|
log.state = log.STARTED
|
||||||
log.script = '# %s\n%s' % (remote_path, script)
|
log.script = '# %s\n%s' % (remote_path, script)
|
||||||
log.save(update_fields=('script', 'state'))
|
log.save(update_fields=('script', 'state'))
|
||||||
|
|
|
@ -150,9 +150,8 @@ class Route(models.Model):
|
||||||
def backend_class(self):
|
def backend_class(self):
|
||||||
return ServiceBackend.get_backend(self.backend)
|
return ServiceBackend.get_backend(self.backend)
|
||||||
|
|
||||||
# TODO rename to get_hosts
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def get_servers(cls, operation, **kwargs):
|
def get_routes(cls, operation, **kwargs):
|
||||||
cache = kwargs.get('cache', {})
|
cache = kwargs.get('cache', {})
|
||||||
if not cache:
|
if not cache:
|
||||||
for route in cls.objects.filter(is_active=True).select_related('host'):
|
for route in cls.objects.filter(is_active=True).select_related('host'):
|
||||||
|
@ -162,19 +161,18 @@ class Route(models.Model):
|
||||||
cache[key].append(route)
|
cache[key].append(route)
|
||||||
except KeyError:
|
except KeyError:
|
||||||
cache[key] = [route]
|
cache[key] = [route]
|
||||||
servers = []
|
routes = []
|
||||||
backend_cls = operation.backend
|
backend_cls = operation.backend
|
||||||
key = (backend_cls.get_name(), operation.action)
|
key = (backend_cls.get_name(), operation.action)
|
||||||
try:
|
try:
|
||||||
routes = cache[key]
|
target_routes = cache[key]
|
||||||
except KeyError:
|
except KeyError:
|
||||||
pass
|
pass
|
||||||
else:
|
else:
|
||||||
for route in routes:
|
for route in target_routes:
|
||||||
if route.matches(operation.instance):
|
if route.matches(operation.instance):
|
||||||
route.host.async = route.async
|
routes.append(route)
|
||||||
servers.append(route.host)
|
return routes
|
||||||
return servers
|
|
||||||
|
|
||||||
def clean(self):
|
def clean(self):
|
||||||
if not self.match:
|
if not self.match:
|
||||||
|
|
|
@ -30,12 +30,12 @@ class RouterTests(BaseTestCase):
|
||||||
|
|
||||||
route = Route.objects.create(backend=backend, host=self.host, match='True')
|
route = Route.objects.create(backend=backend, host=self.host, match='True')
|
||||||
operation = Operation(backend=TestBackend, instance=route, action='save')
|
operation = Operation(backend=TestBackend, instance=route, action='save')
|
||||||
self.assertEqual(1, len(Route.get_servers(operation)))
|
self.assertEqual(1, len(Route.get_routes(operation)))
|
||||||
|
|
||||||
route = Route.objects.create(backend=backend, host=self.host1,
|
route = Route.objects.create(backend=backend, host=self.host1,
|
||||||
match='route.backend == "%s"' % TestBackend.get_name())
|
match='route.backend == "%s"' % TestBackend.get_name())
|
||||||
self.assertEqual(2, len(Route.get_servers(operation)))
|
self.assertEqual(2, len(Route.get_routes(operation)))
|
||||||
|
|
||||||
route = Route.objects.create(backend=backend, host=self.host2,
|
route = Route.objects.create(backend=backend, host=self.host2,
|
||||||
match='route.backend == "something else"')
|
match='route.backend == "something else"')
|
||||||
self.assertEqual(2, len(Route.get_servers(operation)))
|
self.assertEqual(2, len(Route.get_routes(operation)))
|
||||||
|
|
|
@ -84,6 +84,9 @@ class AttrDict(dict):
|
||||||
super(AttrDict, self).__init__(*args, **kwargs)
|
super(AttrDict, self).__init__(*args, **kwargs)
|
||||||
self.__dict__ = self
|
self.__dict__ = self
|
||||||
|
|
||||||
|
def __hash__(self):
|
||||||
|
return hash(id(self))
|
||||||
|
|
||||||
|
|
||||||
class CaptureStdout(list):
|
class CaptureStdout(list):
|
||||||
def __enter__(self):
|
def __enter__(self):
|
||||||
|
|
Loading…
Reference in a new issue