Merge pull request #116 from pigmej/jnowak/pluggable_transports

Pluggable transports
This commit is contained in:
Łukasz Oleś 2015-09-08 15:34:56 +02:00
commit f4a23916c8
59 changed files with 421 additions and 155 deletions

View File

@ -1,5 +1,5 @@
- hosts: [{{ ip }}]
- hosts: [{{host}}]
sudo: yes
tasks:
- shell: echo `/sbin/ifconfig`

View File

@ -1,5 +1,5 @@
- hosts: [{{ ip }}]
- hosts: [{{host}}]
sudo: yes
tasks:
- shell: docker stop {{ resource_name }}

View File

@ -1,4 +1,4 @@
- hosts: [{{ ip }}]
- hosts: [{{host}}]
sudo: yes
tasks:
- docker:

View File

@ -1,5 +1,5 @@
- hosts: [{{ ip }}]
- hosts: [{{host}}]
sudo: yes
tasks:
- shell: docker --version

View File

@ -1,5 +1,5 @@
- hosts: [{{ ip }}]
- hosts: [{{host}}]
sudo: yes
tasks:
- shell: docker stop {{ resource_name }}

View File

@ -1,5 +1,5 @@
- hosts: [{{ ip }}]
- hosts: [{{host}}]
sudo: yes
tasks:
- docker:

View File

@ -1,4 +1,4 @@
- hosts: [{{ ip }}]
- hosts: [{{host}}]
sudo: yes
tasks:
- name: glance api container

View File

@ -1,5 +1,5 @@
- hosts: [{{ ip }}]
- hosts: [{{host}}]
sudo: yes
tasks:
- docker:

View File

@ -1,5 +1,5 @@
- hosts: [{{ ip }}]
- hosts: [{{host}}]
sudo: yes
tasks:
- shell: echo 'removed'

View File

@ -1,4 +1,4 @@
- hosts: [{{ ip }}]
- hosts: [{{host}}]
sudo: yes
vars:
ip: {{ ip }}
@ -26,12 +26,12 @@
- file: path={{ config_dir.value['src'] }}/policy.json state=touch
- file: path={{ config_dir.value['src'] }}/schema-image.json state=touch
- file: path={{ config_dir.value['src'] }}/exports state=touch
- template: src={{ resource_dir }}/templates/glance-api.conf dest={{ config_dir.value['src'] }}/glance-api.conf
- template: src={{ resource_dir }}/templates/glance-api-paste.ini dest={{ config_dir.value['src'] }}/glance-api-paste.ini
- template: src={{ resource_dir }}/templates/glance-cache.conf dest={{ config_dir.value['src'] }}/glance-cache.conf
- template: src={{ resource_dir }}/templates/glance-registry.conf dest={{ config_dir.value['src'] }}/glance-registry.conf
- template: src={{ resource_dir }}/templates/glance-registry-paste.ini dest={{ config_dir.value['src'] }}/glance-registry-paste.ini
- template: src={{ resource_dir }}/templates/glance-scrubber.conf dest={{ config_dir.value['src'] }}/glance-scrubber.conf
- template: src={{ resource_dir }}/templates/policy.json dest={{ config_dir.value['src'] }}/policy.json
- template: src={{ resource_dir }}/templates/schema-image.json dest={{ config_dir.value['src'] }}/schema-image.json
- template: src={{ resource_dir }}/templates/exports dest={{ config_dir.value['src'] }}/glance-export
- template: src={{templates_dir}}/glance-api.conf dest={{ config_dir.value['src'] }}/glance-api.conf
- template: src={{templates_dir}}/glance-api-paste.ini dest={{ config_dir.value['src'] }}/glance-api-paste.ini
- template: src={{templates_dir}}/glance-cache.conf dest={{ config_dir.value['src'] }}/glance-cache.conf
- template: src={{templates_dir}}/glance-registry.conf dest={{ config_dir.value['src'] }}/glance-registry.conf
- template: src={{templates_dir}}/glance-registry-paste.ini dest={{ config_dir.value['src'] }}/glance-registry-paste.ini
- template: src={{templates_dir}}/glance-scrubber.conf dest={{ config_dir.value['src'] }}/glance-scrubber.conf
- template: src={{templates_dir}}/policy.json dest={{ config_dir.value['src'] }}/policy.json
- template: src={{templates_dir}}/schema-image.json dest={{ config_dir.value['src'] }}/schema-image.json
- template: src={{templates_dir}}/exports dest={{ config_dir.value['src'] }}/glance-export

View File

@ -1,4 +1,4 @@
- hosts: [{{ ip }}]
- hosts: [{{host}}]
sudo: yes
tasks:
- name: glance registry container

View File

@ -1,5 +1,5 @@
- hosts: [{{ ip }}]
- hosts: [{{host}}]
sudo: yes
tasks:
- docker:

View File

@ -1,5 +1,5 @@
# TODO
- hosts: [{{ ip }}]
- hosts: [{{host}}]
sudo: yes
tasks:
- file: path={{ config_dir.value['src'] }} state=absent

View File

@ -1,5 +1,5 @@
# TODO
- hosts: [{{ ip }}]
- hosts: [{{host}}]
sudo: yes
vars:
config_dir: {src: {{ config_dir.value['src'] }}, dst: {{ config_dir.value['dst'] }}}
@ -19,4 +19,4 @@
tasks:
- file: path={{ config_dir.value['src'] }}/ state=directory
- file: path={{ config_dir.value['src'] }}/haproxy.cfg state=touch
- template: src={{ resource_dir }}/templates/haproxy.cfg dest=/etc/haproxy/haproxy.cfg
- template: src={{templates_dir}}/haproxy.cfg dest=/etc/haproxy/haproxy.cfg

View File

@ -19,4 +19,4 @@
tasks:
- file: path={{ config_dir.value['src'] }}/ state=directory
- file: path={{ config_dir.value['src'] }}/haproxy.cfg state=touch
- template: src={{ resource_dir }}/templates/haproxy.cfg dest=/etc/haproxy/haproxy.cfg
- template: src={{templates_dir}}/haproxy.cfg dest=/etc/haproxy/haproxy.cfg

View File

@ -0,0 +1,10 @@
- hosts: [{{host}}]
sudo: yes
tasks:
- apt:
name: haproxy
state: present
- replace:
dest: '/etc/default/haproxy'
regexp: ENABLED=0
replace: ENABLED=1

View File

@ -1,4 +1,4 @@
- hosts: [{{ ip }}]
- hosts: [{{host}}]
sudo: yes
tasks:
- name: haproxy container

View File

@ -1,4 +1,4 @@
- hosts: [{{ ip }}]
- hosts: [{{host}}]
sudo: yes
tasks:
- apt:

View File

@ -1,5 +1,5 @@
- hosts: [{{ ip }}]
- hosts: [{{host}}]
sudo: yes
tasks:
- service:

View File

@ -1,4 +1,4 @@
- hosts: {{ip}}
- hosts: [{{host}}]
sudo: yes
tasks:
{% for ip, host in zip(hosts_ips.value, hosts_names.value) %}

View File

@ -1,4 +1,4 @@
- hosts: [{{ ip }}]
- hosts: [{{host}}]
sudo: yes
tasks:
- file: path={{config_dir}} state=absent

View File

@ -1,4 +1,4 @@
- hosts: [{{ ip }}]
- hosts: [{{host}}]
sudo: yes
vars:
admin_token: {{admin_token}}
@ -10,8 +10,8 @@
db_name: {{db_name}}
tasks:
- file: path={{config_dir}} state=directory
- template: src={{resource_dir}}/templates/keystone.conf dest={{config_dir}}/keystone.conf
- template: src={{resource_dir}}/templates/default_catalog.templates dest={{config_dir}}/default_catalog.templates
- template: src={{resource_dir}}/templates/logging.conf dest={{config_dir}}/logging.conf
- template: src={{resource_dir}}/templates/policy.json dest={{config_dir}}/policy.json
- template: src={{resource_dir}}/templates/exports dest={{ config_dir }}/keystone-exports
- template: src={{templates_dir}}/keystone.conf dest={{config_dir}}/keystone.conf
- template: src={{templates_dir}}/default_catalog.templates dest={{config_dir}}/default_catalog.templates
- template: src={{templates_dir}}/logging.conf dest={{config_dir}}/logging.conf
- template: src={{templates_dir}}/policy.json dest={{config_dir}}/policy.json
- template: src={{templates_dir}}/exports dest={{ config_dir }}/keystone-exports

View File

@ -1,4 +1,4 @@
- hosts: [{{ ip }}]
- hosts: [{{host}}]
sudo: yes
tasks:
- name: keystone role

View File

@ -1,4 +1,4 @@
- hosts: [{{ ip }}]
- hosts: [{{host}}]
sudo: yes
tasks:
- name: install python-keystoneclient

View File

@ -1,5 +1,5 @@
# TODO
- hosts: [{{ ip }}]
- hosts: [{{host}}]
sudo: yes
tasks:
- name: keystone container

View File

@ -1,4 +1,4 @@
- hosts: [{{ ip }}]
- hosts: [{{host}}]
sudo: yes
tasks:
- name: keystone container

View File

@ -1,4 +1,4 @@
- hosts: [{{ ip }}]
- hosts: [{{host}}]
sudo: yes
tasks:
- name: keystone tenant

View File

@ -1,4 +1,4 @@
- hosts: [{{ ip }}]
- hosts: [{{host}}]
sudo: yes
tasks:
- name: install python-keystoneclient

View File

@ -1,4 +1,4 @@
- hosts: [{{ ip }}]
- hosts: [{{host}}]
sudo: yes
tasks:
- name: keystone user

View File

@ -1,4 +1,4 @@
- hosts: [{{ ip }}]
- hosts: [{{host}}]
sudo: yes
tasks:
- name: install python-keystoneclient

View File

@ -1,4 +1,4 @@
- hosts: [{{ ip }}]
- hosts: [{{host}}]
sudo: yes
tasks:
- name: mariadb db

View File

@ -1,4 +1,4 @@
- hosts: [{{ ip }}]
- hosts: [{{host}}]
sudo: yes
tasks:
- name: mariadb db

View File

@ -1,4 +1,4 @@
- hosts: [{{ ip }}]
- hosts: [{{host}}]
sudo: yes
tasks:
- name: mariadb container

View File

@ -1,4 +1,4 @@
- hosts: [{{ ip }}]
- hosts: [{{host}}]
sudo: yes
tasks:
- name: mariadb container

View File

@ -1,4 +1,4 @@
- hosts: [{{ ip }}]
- hosts: [{{host}}]
sudo: yes
tasks:
- name: mariadb user

View File

@ -1,4 +1,4 @@
- hosts: [{{ ip }}]
- hosts: [{{host}}]
sudo: yes
tasks:
- name: mariadb user

View File

@ -7,4 +7,4 @@
keystone_host: {{keystone_host}}
keystone_port: {{keystone_port}}
tasks:
- template: src={{resource_dir}}/templates/openrc.template dest=/root/openrc
- template: src={{templates_dir}}/openrc.template dest=/root/openrc

View File

@ -1,4 +1,4 @@
- hosts: [{{ ip }}]
- hosts: [{{host}}]
sudo: yes
tasks:
- file: path={{config_dir}} state=absent

View File

@ -1,8 +1,8 @@
- hosts: [{{ ip }}]
- hosts: [{{host}}]
sudo: yes
vars:
admin_user: {{admin_user}}
admin_password: {{admin_password}}
tasks:
- file: path={{config_dir}} state=directory
- template: src={{resource_dir}}/templates/rabbitmq.conf dest={{config_dir}}/rabbitmq.conf
- template: src={{templates_dir}}/rabbitmq.conf dest={{config_dir}}/rabbitmq.conf

View File

@ -1,4 +1,4 @@
- hosts: [{{ ip }}]
- hosts: [{{host}}]
sudo: yes
tasks:
- rabbitmq_user: user={{user_name}}

View File

@ -1,4 +1,4 @@
- hosts: [{{ ip }}]
- hosts: [{{host}}]
sudo: yes
tasks:
- rabbitmq_user: user={{user_name}}

View File

@ -1,4 +1,4 @@
- hosts: [{{ ip }}]
- hosts: [{{host}}]
sudo: yes
tasks:
- rabbitmq_vhost: name={{vhost_name}}

View File

@ -1,4 +1,4 @@
- hosts: [{{ ip }}]
- hosts: [{{host}}]
sudo: yes
tasks:
- rabbitmq_vhost: name={{vhost_name}}

View File

@ -1,4 +1,4 @@
- hosts: [{{ip}}]
- hosts: [{{host}}]
sudo: yes
tasks:
- shell: riak-admin cluster join {{join_to}}

View File

@ -1,4 +1,4 @@
- hosts: [{{ip}}]
- hosts: [{{host}}]
sudo: yes
tasks:
# - shell: sleep 30

View File

@ -1,4 +1,4 @@
- hosts: [{{ip}}]
- hosts: [{{host}}]
sudo: yes
tasks:
- shell: riak-admin cluster join {{join_to}}

View File

@ -1,4 +1,4 @@
- hosts: [{{ip}}]
- hosts: [{{host}}]
sudo: yes
tasks:
- apt:

View File

@ -1,4 +1,4 @@
- hosts: [{{ip}}]
- hosts: [{{host}}]
sudo: yes
tasks:
# those below are mostly for tests
@ -18,7 +18,7 @@
state: stopped
- file: path=/etc/riak/riak.conf state=touch
- template:
src: {{ resource_dir }}/templates/riak.conf
src: {{templates_dir}}/riak.conf
dest: /etc/riak/riak.conf
- shell: rm -fr /var/lib/riak/kv_vnode/*
- shell: rm -fr /var/lib/riak/ring/*

View File

@ -5,7 +5,7 @@
name: riak
state: stopped
- template:
src: {{ resource_dir }}/templates/riak.conf
src: {{templates_dir}}/riak.conf
dest: /etc/riak/riak.conf
- service:
name: riak

View File

@ -15,3 +15,6 @@ input:
ssh_user:
schema: str!
value:
name:
schema: str
value: a node

View File

@ -1,10 +1,18 @@
# -*- coding: UTF-8 -*-
# -*- coding: utf-8 -*-
import handlers
from solar.core.transports.ssh import SSHSyncTransport, SSHRunTransport
# from solar.core.transports.rsync import RsyncSyncTransport
_default_transports = {
'sync': SSHSyncTransport,
# 'sync': RsyncSyncTransport,
'run': SSHRunTransport
}
def resource_action(resource, action):
handler = resource.metadata.get('handler', 'none')
with handlers.get(handler)([resource]) as h:
with handlers.get(handler)([resource], _default_transports) as h:
return h.action(resource, action)

View File

@ -28,6 +28,8 @@ class AnsiblePlaybook(base.BaseHandler):
provider.directory, ROLES_PATH))
def action(self, resource, action):
# This would require to put this file to remote and execute it (mostly)
log.debug("Ansible playbook is not ported to pluggable transports")
action_file = os.path.join(
resource.metadata['actions_path'],
resource.metadata['actions'][action])

View File

@ -11,20 +11,35 @@ from solar import errors
# otherwise fabric will sys.exit(1) in case of errors
env.warn_only = True
# if we would have something like solard that would render this then
# we would not need to render it there
# for now we redender it locally, sync to remote, run ansible on remote host as local
class AnsibleTemplate(TempFileHandler):
def action(self, resource, action_name):
inventory_file = self._create_inventory(resource)
playbook_file = self._create_playbook(resource, action_name)
log.debug('inventory_file: %s', inventory_file)
log.debug('playbook_file: %s', playbook_file)
call_args = ['ansible-playbook', '--module-path', '/vagrant/library', '-i', inventory_file, playbook_file]
# self.transport_sync.copy(resource, self.dirs[resource.name], self.dirs[resource.name])
self._copy_templates_and_scripts(resource, action_name)
self.transport_sync.copy(resource, self.dst, '/tmp')
self.transport_sync.copy(resource, '/vagrant/library', '/tmp')
self.transport_sync.sync_all()
call_args = ['ansible-playbook', '--module-path', '/tmp/library', '-i', inventory_file, playbook_file]
log.debug('EXECUTING: %s', ' '.join(call_args))
with fabric_api.shell_env(ANSIBLE_HOST_KEY_CHECKING='False'):
out = fabric_api.local(' '.join(call_args), capture=True)
out = self.transport_run.run(resource, *call_args)
log.debug(out)
if out.failed:
raise errors.SolarError(out)
# with fabric_api.shell_env(ANSIBLE_HOST_KEY_CHECKING='False'):
# out = fabric_api.local(' '.join(call_args), capture=True)
# if out.failed:
# raise errors.SolarError(out)
def _create_inventory(self, r):
directory = self.dirs[r.name]
@ -34,15 +49,25 @@ class AnsibleTemplate(TempFileHandler):
return inventory_path
def _render_inventory(self, r):
inventory = '{0} ansible_ssh_host={1} ansible_connection=ssh ansible_ssh_user={2} ansible_ssh_private_key_file={3} {4}'
host, user, ssh_key = r.args['ip'].value, r.args['ssh_user'].value, r.args['ssh_key'].value
# inventory = '{0} ansible_ssh_host={1} ansible_connection=ssh ansible_ssh_user={2} ansible_ssh_private_key_file={3} {4}'
# host, user, ssh_key = r.args['ip'].value, r.args['ssh_user'].value, r.args['ssh_key'].value
# XXX: r.args['ssh_user'] should be something different in this case probably
inventory = '{0} ansible_connection=local user={1} {2}'
host, user = 'localhost', r.args['ssh_user'].value
args = []
for arg in r.args:
args.append('{0}="{1}"'.format(arg, r.args[arg].value))
args = ' '.join(args)
inventory = inventory.format(host, host, user, ssh_key, args)
inventory = inventory.format(host, user, args)
log.debug(inventory)
return inventory
def _create_playbook(self, resource, action):
return self._compile_action_file(resource, action)
def _make_args(self, resource):
args = super(AnsibleTemplate, self)._make_args(resource)
args['host'] = 'localhost'
return args

View File

@ -6,12 +6,21 @@ import tempfile
from jinja2 import Template
from solar.core.log import log
from solar.core.transports.ssh import SSHSyncTransport, SSHRunTransport
class BaseHandler(object):
def __init__(self, resources):
def __init__(self, resources, handlers=None):
self.resources = resources
if handlers is None:
self.transport_sync = SSHSyncTransport()
self.transport_run = SSHRunTransport()
else:
self.transport_run = handlers['run']()
self.transport_sync = handlers['sync']()
self.transport_sync.bind_with(self.transport_run)
self.transport_run.bind_with(self.transport_sync)
def __enter__(self):
return self
@ -21,9 +30,9 @@ class BaseHandler(object):
class TempFileHandler(BaseHandler):
def __init__(self, resources):
def __init__(self, resources, handlers=None):
super(TempFileHandler, self).__init__(resources, handlers)
self.dst = tempfile.mkdtemp()
self.resources = resources
def __enter__(self):
self.dirs = {}
@ -58,9 +67,39 @@ class TempFileHandler(BaseHandler):
tpl = Template(f.read())
return tpl.render(str=str, zip=zip, **args)
def _copy_templates_and_scripts(self, resource, action):
# TODO: we might need to optimize it later, like provide list
# templates/scripts per action
log.debug("Adding templates for %s %s", resource.name, action)
trg_templates_dir = None
trg_scripts_dir = None
base_path = resource.metadata['base_path']
src_templates_dir = os.path.join(base_path, 'templates')
if os.path.exists(src_templates_dir):
trg_templates_dir = os.path.join(self.dirs[resource.name], 'templates')
shutil.copytree(src_templates_dir, trg_templates_dir)
src_scripts_dir = os.path.join(base_path, 'scripts')
if os.path.exists(src_scripts_dir):
trg_scripts_dir = os.path.join(self.dirs[resource.name], 'scripts')
shutil.copytree(src_scripts_dir, trg_scripts_dir)
return (trg_templates_dir, trg_scripts_dir)
def prepare_templates_and_scripts(self, resource, action, target_dir=None):
target_dir = target_dir or self.dirs[resource.name]
templates, scripts = self._copy_templates_and_scripts(resource, action)
if templates:
self.transport_sync.copy(resource, templates, target_dir)
if scripts:
self.transport_sync.copy(resource, scripts, target_dir)
def _make_args(self, resource):
args = {'resource_name': resource.name}
args['resource_dir'] = resource.metadata['base_path']
args['templates_dir'] = 'templates/'
args['scripts_dir'] = 'scripts/'
args.update(resource.args)
return args

View File

@ -12,74 +12,12 @@ from solar.core.provider import GitProvider
from solar import errors
class ResourceSSHMixin(object):
@staticmethod
def _ssh_command(resource, *args, **kwargs):
log.debug('SSH: %s', args)
executor = fabric_api.run
if kwargs.get('use_sudo', False):
executor = fabric_api.sudo
managers = [
fabric_api.settings(**ResourceSSHMixin._fabric_settings(resource)),
]
if 'cwd' in kwargs:
managers.append(
fabric_api.cd(kwargs['cwd'])
)
if 'env' in kwargs:
managers.append(
fabric_api.shell_env(**kwargs['env'])
)
if 'warn_only' in kwargs:
managers.append(
fabric_api.warn_only())
with nested(*managers):
return executor(' '.join(args))
@staticmethod
def _scp_command(resource, _from, _to, use_sudo=False):
log.debug('SCP: %s -> %s', _from, _to)
executor = partial(
fabric_project.upload_project,
remote_dir=_to,
local_dir=_from,
use_sudo=use_sudo
)
if os.path.isfile(_from):
executor = partial(
fabric_project.put,
remote_path=_to,
local_path=_from,
use_sudo=use_sudo
)
with fabric_api.settings(**ResourceSSHMixin._fabric_settings(resource)):
return executor()
@staticmethod
def _fabric_settings(resource):
return {
'host_string': ResourceSSHMixin._ssh_command_host(resource),
'key_filename': resource.args['ssh_key'].value,
}
@staticmethod
def _ssh_command_host(resource):
return '{}@{}'.format(resource.args['ssh_user'].value,
resource.args['ip'].value)
class LibrarianPuppet(ResourceSSHMixin):
def __init__(self, resource, organization='openstack'):
class LibrarianPuppet(object):
def __init__(self, resource, organization='openstack', transport_sync=None, transport_run=None):
self.resource = resource
self.organization = organization
self.transport_sync = transport_sync
self.transport_run = transport_run
def install(self):
puppet_module = '{}-{}'.format(
@ -87,7 +25,7 @@ class LibrarianPuppet(ResourceSSHMixin):
self.resource.metadata['puppet_module']
)
puppetlabs = self._ssh_command(
puppetlabs = self.transport_run.run(
self.resource,
'sudo', 'cat', '/var/tmp/puppet/Puppetfile'
)
@ -123,14 +61,16 @@ class LibrarianPuppet(ResourceSSHMixin):
f.write('\n'.join(modules))
f.write('\n')
self._scp_command(
self.transport_sync.copy(
self.resource,
'/tmp/Puppetfile',
'/var/tmp/puppet/Puppetfile',
use_sudo=True
)
self._ssh_command(
self.transport_sync.sync_all()
self.transport_run.run(
self.resource,
'sudo', 'librarian-puppet', 'install',
cwd='/var/tmp/puppet'
@ -142,7 +82,7 @@ class LibrarianPuppet(ResourceSSHMixin):
# - hiera-redis is installed with the 2.0 fix (https://github.com/GGenie/hiera-redis)
# - redis is installed and cluster set up with master (on slaves set up 'slaveof 10.0.0.2 6379')
# - redis keys are separated by colon (same as in hiera-redis backend)
class Puppet(ResourceSSHMixin, TempFileHandler):
class Puppet(TempFileHandler):
def action(self, resource, action_name):
log.debug('Executing Puppet manifest %s %s', action_name, resource)
@ -151,9 +91,11 @@ class Puppet(ResourceSSHMixin, TempFileHandler):
self.upload_manifests(resource)
self._scp_command(resource, action_file, '/tmp/action.pp')
self.prepare_templates_and_scripts(resource, action_file, '')
self.transport_sync.copy(resource, action_file, '/tmp/action.pp')
self.transport_sync.sync_all()
cmd = self._ssh_command(
cmd = self.transport_run.run(
resource,
'puppet', 'apply', '-vd', '/tmp/action.pp', '--detailed-exitcodes',
env={
@ -185,7 +127,7 @@ class Puppet(ResourceSSHMixin, TempFileHandler):
forge = resource.args['forge'].value
# Check if module already installed
modules = self._ssh_command(
modules = self.transport_run.run(
resource,
'sudo', 'puppet', 'module', 'list'
)
@ -197,7 +139,7 @@ class Puppet(ResourceSSHMixin, TempFileHandler):
break
if not module_installed:
self._ssh_command(
self.transport_run.run(
resource,
'sudo', 'puppet', 'module', 'install', forge
)
@ -205,7 +147,9 @@ class Puppet(ResourceSSHMixin, TempFileHandler):
log.debug('Skipping module installation, already installed')
def upload_manifests_librarian(self, resource):
librarian = LibrarianPuppet(resource)
librarian = LibrarianPuppet(resource,
transport_run=self.transport_run,
transport_sync=self.transport_sync)
librarian.install()
def upload_manifests_git(self, resource):
@ -214,16 +158,18 @@ class Puppet(ResourceSSHMixin, TempFileHandler):
module_directory = '/etc/puppet/modules/{}'.format(
resource.metadata['puppet_module']
)
self._ssh_command(
self.transport_run.run(
resource,
'sudo', 'rm', '-Rf', module_directory
)
self._ssh_command(
self.transport_run.run(
resource, 'sudo', 'mkdir', '-p', module_directory
)
self._scp_command(resource, manifests_path, '/tmp')
self._ssh_command(
self.transport_sync.copy(resource, manifests_path, '/tmp')
self.transport_sync.sync_all()
self.transport_run.run(
resource,
'sudo', 'mv',
'/tmp/{}/*'.format(os.path.split(manifests_path)[1]),

View File

View File

@ -0,0 +1,90 @@
class Executor(object):
def __init__(self, resource, executor, params=None):
"""
:param resource: solar resource
:param executor: callable executor, that will perform action
:param params: optional argument
that migth be used later for decomposition etc
"""
self.resource = resource
self.params = params
self._executor = executor
self._valid = True
@property
def valid(self):
return self._valid
@valid.setter
def valid(self, value):
self._valid = value
def run(self, transport):
if self.valid:
self._executor(transport)
class SyncTransport(object):
"""
Transport that is responsible for file / directory syncing.
"""
def __init__(self):
self.executors = []
def bind_with(self, other):
# we migth add there something later
# like compat checking etc
self.other = other
def copy(self, resource, *args, **kwargs):
pass
def preprocess(self, executor):
# we can check there if we need to run sync executor or not
# ideally would be to do so on other side
# it may set executor.valid to False then executor will be skipped
pass
def preprocess_all(self):
# we cat use there md5 for big files to check if we need to sync it
# or if remote is still valid
# we can run that in parallell also
# can be also used to prepare files for further transfer
for executor in self.executors:
self.preprocess(executor)
def run_all(self):
for executor in self.executors:
executor.run(self)
def sync_all(self):
"""
It checks if action is required first,
then runs all sequentially.
Could be someday changed to parallel thing.
"""
self.preprocess_all()
self.run_all()
self.executors = [] # clear after all
class RunTransport(object):
"""
Transport that is responsible for executing remote commands, rpc like thing.
"""
def __init__(self):
pass
def bind_with(self, other):
# we migth add there something later
# like compat checking etc
self.other = other
def run(self, resource, *args, **kwargs):
pass
def __call__(self, *args, **kwargs):
return self.run(*args, **kwargs)

View File

@ -0,0 +1,48 @@
import os
from functools import partial
from contextlib import nested
from fabric import api as fabric_api
from solar.core.log import log
from solar.core.transports.base import SyncTransport, Executor
class RsyncSyncTransport(SyncTransport):
def _rsync_props(self, resource):
return {
'ssh_key': resource.args['ssh_key'].value,
'ssh_user': resource.args['ssh_user'].value
}
def _rsync_command_host(self, resource):
return '{}@{}'.format(resource.args['ssh_user'].value,
resource.args['ip'].value)
def copy(self, resource, _from, _to, use_sudo=False):
log.debug("RSYNC: %s -> %s", _from, _to)
if use_sudo:
rsync_path = "sudo rsync"
else:
rsync_path = "rsync"
rsync_props = self._rsync_props(resource)
rsync_cmd = ('rsync -az -e "ssh -i %(ssh_key)s" '
'--rsync-path="%(rsync_path)s" %(_from)s '
'%(rsync_host)s:%(_to)s') % dict(
rsync_path=rsync_path,
ssh_key=rsync_props['ssh_key'],
rsync_host=self._rsync_command_host(resource),
_from=_from,
_to=_to)
rsync_executor = lambda transport: fabric_api.local(
rsync_cmd
)
log.debug("RSYNC CMD: %r" % rsync_cmd)
executor = Executor(resource=resource,
executor=rsync_executor,
params=(_from, _to, use_sudo))
self.executors.append(executor)

View File

@ -0,0 +1,95 @@
import os
from functools import partial
from contextlib import nested
from fabric import api as fabric_api
from fabric.contrib import project as fabric_project
from solar.core.log import log
from solar.core.transports.base import RunTransport, SyncTransport, Executor
class _SSHTransport(object):
# TODO: maybe static/class method ?
def _fabric_settings(self, resource):
return {
'host_string': self._ssh_command_host(resource),
'key_filename': resource.args['ssh_key'].value,
}
# TODO: maybe static/class method ?
def _ssh_command_host(self, resource):
return '{}@{}'.format(resource.args['ssh_user'].value,
resource.args['ip'].value)
class SSHSyncTransport(SyncTransport, _SSHTransport):
def __init__(self):
SyncTransport.__init__(self)
def _copy_file(self, resource, _from, _to, use_sudo=False):
executor = lambda transport: fabric_project.put(
remote_path=_to,
local_path=_from,
use_sudo=use_sudo
)
return executor
def _copy_directory(self, resource, _from, _to, use_sudo=False):
executor = lambda transport: fabric_project.upload_project(
remote_dir=_to,
local_dir=_from,
use_sudo=use_sudo
)
return executor
def copy(self, resource, _from, _to, use_sudo=False):
log.debug('SCP: %s -> %s', _from, _to)
if os.path.isfile(_from):
executor = self._copy_file(resource, _from, _to, use_sudo)
else:
executor = self._copy_directory(resource, _from, _to, use_sudo)
# with fabric_api.settings(**self._fabric_settings(resource)):
# return executor()
executor = Executor(resource=resource,
executor=executor,
params=(_from, _to, use_sudo))
self.executors.append(executor)
def run_all(self):
for executor in self.executors:
resource = executor.resource
with fabric_api.settings(**self._fabric_settings(resource)):
executor.run(self)
class SSHRunTransport(RunTransport, _SSHTransport):
def run(self, resource, *args, **kwargs):
log.debug('SSH: %s', args)
executor = fabric_api.run
if kwargs.get('use_sudo', False):
executor = fabric_api.sudo
managers = [
fabric_api.settings(**self._fabric_settings(resource)),
]
cwd = kwargs.get('cwd')
if cwd:
managers.append(fabric_api.cd(kwargs['cwd']))
env = kwargs.get('env')
if env:
managers.append(fabric_api.shell_env(**kwargs['env']))
if kwargs.get('warn_only', False):
managers.append(fabric_api.warn_only())
with nested(*managers):
return executor(' '.join(args))