This commit is contained in:
Vagrant User 2015-04-15 18:43:02 +00:00
parent 56baf73d05
commit a50f0933c7
23 changed files with 321 additions and 0 deletions

15
README Normal file
View File

@ -0,0 +1,15 @@
Usage:
Creating resources:
from x import resource
node1 = resource.create('node1', 'x/resources/ro_node/', 'rs/', {'ip':'10.0.0.3', 'ssh_key' : '/vagrant/tmp/keys/ssh_private', 'user':'vagrant'})
node2 = resource.create('node2', 'x/resources/ro_node/', 'rs/', {'ip':'10.0.0.4', 'ssh_key' : '/vagrant/tmp/keys/ssh_private', 'user':'vagrant'})
keystone_db_data = resource.create('mariadb_keystone_data', 'x/resources/data_container/', 'rs/', {'image' : 'mariadb', 'export_volumes' : ['/var/lib/mysql'], 'host': '', 'remote_user': '', 'ssh_key': ''}, connections={'host' : 'node2.ip', 'ssh_key':'node2.ssh_key', 'remote_user':'node2.user'})
nova_db_data = resource.create('mariadb_nova_data', 'x/resources/data_container/', 'rs/', {'image' : 'mariadb', 'export_volumes' : ['/var/lib/mysql'], 'host': '', 'remote_user': '', 'ssh_key': ''}, connections={'host' : 'node1.ip', 'ssh_key':'node1.ssh_key', 'remote_user':'node1.user'})
to make connection after resource is created use signal.connect
*** WARNNING ***
Resource DB is stored only in memory, if you close python interpretet you will lost it.
It can be recreated from resources but it's not done yet.
Connections are stored only in memory. It can be easly dumped as JSON file

0
__init__.py Normal file
View File

11
actions.py Normal file
View File

@ -0,0 +1,11 @@
# -*- coding: UTF-8 -*-
import handlers
def resource_action(resource, action):
handler = resource.metadata['handler']
handler = handlers.get(handler)
handler().action(resource, action)
def tag_action(tag, action):
#TODO
pass

11
db.py Normal file
View File

@ -0,0 +1,11 @@
# -*- coding: UTF-8 -*-
RESOURCE_DB = {}
def resource_add(key, value):
if key in RESOURCE_DB:
raise Exception('Key `{0}` already exists'.format(key))
RESOURCE_DB[key] = value
def get_resource(key):
return RESOURCE_DB.get(key, None)

62
handlers.py Normal file
View File

@ -0,0 +1,62 @@
# -*- coding: UTF-8 -*-
import os
import subprocess
import tempfile
from jinja2 import Template
def get(handler_name):
handler = HANDLERS.get(handler_name, None)
if handler:
return handler
raise Exception('Handler {0} does not exist'.format(handler_name))
class Ansible(object):
"""TODO"""
def __init__(self):
pass
def action(self, resource, action):
pass
def _get_connection(self, resource):
return {'ssh_user': '',
'ssh_key': '',
'host': ''}
def _create_inventory(self, dest_dir):
pass
def _create_playbook(self, dest_dir):
pass
class Shell(object):
def __init__(self):
pass
def action(self, resource, action):
action_file = resource.metadata['actions'][action]
action_file = os.path.join(resource.base_dir, action_file)
with open(action_file) as f:
tpl = Template(f.read())
tpl = tpl.render(resource.args)
tmp_file = tempfile.mkstemp(text=True)[1]
with open(tmp_file, 'w') as f:
f.write(tpl)
subprocess.call(['bash', tmp_file])
class Empty(object):
def action(self, resource, action):
pass
HANDLERS = {'ansible' : Ansible,
'shell': Shell,
'none': Empty}

73
resource.py Normal file
View File

@ -0,0 +1,73 @@
# -*- coding: UTF-8 -*-
import os
import shutil
import yaml
import actions
import signals
import db
class Resource(object):
def __init__(self, name, metadata, args, base_dir):
self.name = name
self.base_dir = base_dir
self.metadata = metadata
self.actions = metadata['actions'].keys() if metadata['actions'] else None
self.requires = metadata['input'].keys()
self._validate_args(args)
self.args = args
self.changed = []
def __repr__(self):
return "Resource('name={0}', metadata={1}, args={2}, base_dir='{3}')".format(self.name,
self.metadata,
self.args,
self.base_dir)
def update(self, args):
for key, value in args.iteritems():
resource_key = self.args.get(key, None)
if resource_key:
self.args[key] = value
self.changed.append(key)
signals.notify(self, key, value)
def action(self, action):
if action in self.actions:
actions.resource_action(self, action)
else:
raise Exception('Uuups, action is not available')
def _validate_args(self, args):
for req in self.requires:
if not req in args:
raise Exception('Requirement `{0}` is missing in args'.format(req))
def create(name, base_path, dest_path, args, connections={}):
if not os.path.exists(base_path):
raise Exception('Base resource does not exist: {0}'.format(dest_path))
if not os.path.exists(dest_path):
raise Exception('Dest dir does not exist: {0}'.format(dest_path))
if not os.path.isdir(dest_path):
raise Exception('Dest path is not a directory: {0}'.format(dest_path))
dest_path = os.path.join(dest_path, name)
base_meta_file = os.path.join(base_path, 'meta.yaml')
meta_file = os.path.join(dest_path, 'meta.yaml')
meta = yaml.load(open(base_meta_file).read())
meta['id'] = name
meta['version'] = '1.0.0'
resource = Resource(name, meta, args, dest_path)
signals.assign_connections(resource, connections)
#save
shutil.copytree(base_path, dest_path)
with open(meta_file, 'w') as f:
f.write(yaml.dump(meta))
db.resource_add(name, resource)
return resource

View File

@ -0,0 +1,10 @@
id: data_container
handler: ansible
version: 1.0.0
actions:
run: run.yml
remove: remove.yml
input:
host:
image:
export_volumes:

View File

@ -0,0 +1,6 @@
- hosts: [{{ ip }}]
sudo: yes
tasks:
- shell: docker stop {{ name }}
- shell: docker rm {{ name }}

View File

@ -0,0 +1,6 @@
- hosts: [{{ ip }}]
sudo: yes
tasks:
- shell: docker run -d --net="host" --privileged \
--name {{ name }} {{ image }} /bin/sh

View File

@ -0,0 +1,10 @@
id: docker
type: resource
handler: ansible
version: v1
actions:
run: simple/docker/run.yml
remove: simple/docker/remove.yml
input:
base_image: ubuntu
tags: [n/1]

View File

@ -0,0 +1,9 @@
id: container
handler: ansible
version: 1.0.0
actions:
run: run.yml
remove: remove.yml
input:
image:
volume_binds:

View File

@ -0,0 +1,6 @@
- hosts: [{{ ip }}]
sudo: yes
tasks:
- shell: docker stop {{ name }}
- shell: docker rm {{ name }}

View File

@ -0,0 +1,6 @@
- hosts: [{{ ip }}]
sudo: yes
tasks:
- shell: docker run -d --net="host" --privileged \
--name {{ name }} {{ image }}

8
resources/file/meta.yaml Normal file
View File

@ -0,0 +1,8 @@
id: file
handler: shell
version: 1.0.0
actions:
run: run.sh
remove: remove.sh
input:
path: /tmp/test_file

3
resources/file/remove.sh Normal file
View File

@ -0,0 +1,3 @@
#!/bin/bash
rm {{ path }}

3
resources/file/run.sh Normal file
View File

@ -0,0 +1,3 @@
#!/bin/bash
touch {{ path }}

View File

@ -0,0 +1,9 @@
id: mariadb
handler: ansible
version: 1.0.0
actions:
run: run.yml
remove: remove.yml
input:
image: tutum/mariadq
tags: [n/1]

View File

@ -0,0 +1,6 @@
- hosts: [{{ ip }}]
sudo: yes
tasks:
- shell: docker stop {{ name }}
- shell: docker rm {{ name }}

View File

@ -0,0 +1,6 @@
- hosts: [{{ ip }}]
sudo: yes
tasks:
- shell: docker run -d --net="host" --privileged \
--name {{ name }} {{ image }}

View File

@ -0,0 +1,10 @@
id: mariadb_user
handler: ansible
version: 1.0.0
actions:
run: run.yml
remove: remove.yml
input:
name: name
password: password
users: []

View File

@ -0,0 +1,9 @@
id: mariadb_user
handler: ansible
version: 1.0.0
actions:
run: run.yml
remove: remove.yml
input:
name: name
password: password

View File

@ -0,0 +1,8 @@
id: mariadb
handler: none
version: 1.0.0
actions:
input:
ip:
ssh_key:
user:

34
signals.py Normal file
View File

@ -0,0 +1,34 @@
# -*- coding: UTF-8 -*-
from collections import defaultdict
import db
CLIENTS = defaultdict(lambda: defaultdict(list))
def connect(emitter, reciver, mappings):
for src, dst in mappings:
CLIENTS[emitter.name][src].append((reciver.name, dst))
def notify(source, key, value):
if key in CLIENTS[source.name]:
for client, r_key in CLIENTS[source.name][key]:
resource = db.get_resource(client)
if resource:
resource.update({r_key: value})
else:
#XXX resource deleted?
pass
def assign_connections(reciver, connections):
mappings = defaultdict(list)
for key, dest in connections.iteritems():
resource, r_key = dest.split('.')
resource = db.get_resource(resource)
value = resource.args[r_key]
reciver.args[key] = value
mappings[resource].append((r_key, key))
for resource, r_mappings in mappings.iteritems():
connect(resource, reciver, r_mappings)