Add commands to work with prototype using ansible
Create two directories: mkdir -p tmp/group_vars Use commands to generate ansible playbook solar -a run -r node docker docker_compose some_service solar -a remove -r node docker docker_compose some_service Removal will be done in reversed order
This commit is contained in:
parent
6e80d08f7d
commit
b4ca953cd2
1
.gitignore
vendored
1
.gitignore
vendored
@ -6,3 +6,4 @@
|
|||||||
# vagrant
|
# vagrant
|
||||||
.vagrant
|
.vagrant
|
||||||
|
|
||||||
|
tmp/
|
||||||
|
@ -1,13 +0,0 @@
|
|||||||
actions:
|
|
||||||
- id: erase_node
|
|
||||||
tasks:
|
|
||||||
- shell: erase
|
|
||||||
- shell: reboot
|
|
||||||
- id: provision_centos
|
|
||||||
tasks:
|
|
||||||
- shell: fuel-agent --image
|
|
||||||
- shell: reboot
|
|
||||||
- id: deploy_compute
|
|
||||||
tasks:
|
|
||||||
- shell: deploy_compute
|
|
||||||
|
|
@ -2,3 +2,7 @@
|
|||||||
|
|
||||||
# how to control other services by pacemaker/corosync? if they will
|
# how to control other services by pacemaker/corosync? if they will
|
||||||
# be installed in containers
|
# be installed in containers
|
||||||
|
|
||||||
|
|
||||||
|
# how we will manage primary and non-primary analog or resources?
|
||||||
|
# maybe they should be just assigned by user ?
|
||||||
|
@ -1,12 +0,0 @@
|
|||||||
---
|
|
||||||
|
|
||||||
id: compose
|
|
||||||
type: resource
|
|
||||||
handler: playbook
|
|
||||||
version: v1
|
|
||||||
run:
|
|
||||||
- shell: docker-compose -f {{compose.item}} -d
|
|
||||||
parameters:
|
|
||||||
item:
|
|
||||||
required: true
|
|
||||||
default: /some/path.yml
|
|
@ -19,9 +19,11 @@ actions:
|
|||||||
register: docker_version
|
register: docker_version
|
||||||
- shell: curl -sSL https://get.docker.com/ubuntu/ | sudo sh
|
- shell: curl -sSL https://get.docker.com/ubuntu/ | sudo sh
|
||||||
when: docker_version|failed
|
when: docker_version|failed
|
||||||
- shell: docker pull debian:jessie
|
- shell: docker pull {{ docker.base_image }}
|
||||||
remove:
|
remove:
|
||||||
- hosts: [docker]
|
- hosts: [docker]
|
||||||
sudo: yes
|
sudo: yes
|
||||||
tasks:
|
tasks:
|
||||||
- shell: apt-get remove -y docker
|
- shell: apt-get remove -y lxc-docker
|
||||||
|
input:
|
||||||
|
base_image: ubuntu
|
||||||
|
23
schema/resources/docker_compose.yml
Normal file
23
schema/resources/docker_compose.yml
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
---
|
||||||
|
|
||||||
|
|
||||||
|
id: docker_compose
|
||||||
|
type: resource
|
||||||
|
handler: ansible
|
||||||
|
version: v1
|
||||||
|
actions:
|
||||||
|
run:
|
||||||
|
- hosts: [docker_compose]
|
||||||
|
sudo: yes
|
||||||
|
tasks:
|
||||||
|
- shell: docker-compose --version
|
||||||
|
register: compose
|
||||||
|
ignore_errors: true
|
||||||
|
- shell: curl -L https://github.com/docker/compose/releases/download/1.1.0/docker-compose-`uname -s`-`uname -m` > /usr/local/bin/docker-compose
|
||||||
|
when: compose|failed
|
||||||
|
- shell: chmod +x /usr/local/bin/docker-compose
|
||||||
|
remove:
|
||||||
|
- hosts: [docker_compose]
|
||||||
|
sudo: yes
|
||||||
|
tasks:
|
||||||
|
- shell: rm -rf /usr/local/bin/docker-compose
|
@ -1,14 +0,0 @@
|
|||||||
---
|
|
||||||
|
|
||||||
id: images
|
|
||||||
type: resource
|
|
||||||
driver: playbook
|
|
||||||
version: v1
|
|
||||||
run:
|
|
||||||
- shell: docker pull {{item}}
|
|
||||||
with_items: images.list
|
|
||||||
parameters:
|
|
||||||
list:
|
|
||||||
required: true
|
|
||||||
another:
|
|
||||||
default: 1
|
|
@ -1,11 +0,0 @@
|
|||||||
---
|
|
||||||
|
|
||||||
id: networks
|
|
||||||
type: resource
|
|
||||||
handler: custom_network_schema
|
|
||||||
version: v1
|
|
||||||
|
|
||||||
# run part will be generated
|
|
||||||
|
|
||||||
# what kind of parameters?
|
|
||||||
|
|
12
schema/resources/node.yml
Normal file
12
schema/resources/node.yml
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
---
|
||||||
|
|
||||||
|
id: node
|
||||||
|
type: resource
|
||||||
|
handler: ansible
|
||||||
|
version: v1
|
||||||
|
input:
|
||||||
|
ssh_host: 127.0.0.1
|
||||||
|
ssh_port: 2222
|
||||||
|
name: first
|
||||||
|
user: vagrant
|
||||||
|
key: .vagrant/machines/default/virtualbox/private_key
|
@ -1,25 +0,0 @@
|
|||||||
---
|
|
||||||
|
|
||||||
|
|
||||||
id: network_eth
|
|
||||||
handler: network_schema
|
|
||||||
parameters:
|
|
||||||
networks:
|
|
||||||
- name: bbc01
|
|
||||||
cidr: 10.0.0.4/24
|
|
||||||
ip_ranges: []
|
|
||||||
provides: [network.ips]
|
|
||||||
|
|
||||||
----
|
|
||||||
id: network_infiniband
|
|
||||||
handler: playbook // infiniband_schema
|
|
||||||
|
|
||||||
|
|
||||||
----
|
|
||||||
|
|
||||||
id: network_check
|
|
||||||
handler: playbook
|
|
||||||
run:
|
|
||||||
- shell: ping {{item}}
|
|
||||||
with_items: network.ips
|
|
||||||
|
|
@ -1,10 +0,0 @@
|
|||||||
|
|
||||||
- id: nic
|
|
||||||
parameters:
|
|
||||||
first:
|
|
||||||
default: 1
|
|
||||||
events:
|
|
||||||
- run:
|
|
||||||
shell: echo 3
|
|
||||||
|
|
||||||
fuel execute --res nic --action run --node 1,2,3
|
|
25
schema/resources/some_service.yml
Normal file
25
schema/resources/some_service.yml
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
---
|
||||||
|
|
||||||
|
|
||||||
|
id: some_service
|
||||||
|
type: resource
|
||||||
|
handler: ansible
|
||||||
|
version: v1
|
||||||
|
actions:
|
||||||
|
run:
|
||||||
|
- hosts: [some_service]
|
||||||
|
sudo: yes
|
||||||
|
tasks:
|
||||||
|
- shell: docker run --name {{ some_service.name }} --net="host"
|
||||||
|
--privileged -d {{ some_service.image }}
|
||||||
|
nc -l {{ some_service.port }}
|
||||||
|
remove:
|
||||||
|
- hosts: [some_service]
|
||||||
|
sudo: yes
|
||||||
|
tasks:
|
||||||
|
- shell: docker stop {{ some_service.name }}
|
||||||
|
- shell: docker rm {{some_service.name}}
|
||||||
|
input:
|
||||||
|
image: "{{ docker.base_image }}"
|
||||||
|
port: 8800
|
||||||
|
name: shining_star
|
@ -1,2 +1,3 @@
|
|||||||
six>=1.9.0
|
six>=1.9.0
|
||||||
|
pyyaml
|
||||||
|
jinja2
|
||||||
|
@ -13,5 +13,53 @@
|
|||||||
# under the License.
|
# under the License.
|
||||||
|
|
||||||
|
|
||||||
|
import sys
|
||||||
|
|
||||||
|
import argparse
|
||||||
|
import yaml
|
||||||
|
|
||||||
|
from solar.core import ansible
|
||||||
|
from solar.interfaces.db import Storage
|
||||||
|
|
||||||
|
|
||||||
|
def parse():
|
||||||
|
|
||||||
|
parser = argparse.ArgumentParser()
|
||||||
|
|
||||||
|
parser.add_argument(
|
||||||
|
'-a',
|
||||||
|
'--action',
|
||||||
|
help='action to execute',
|
||||||
|
required=True)
|
||||||
|
|
||||||
|
parser.add_argument(
|
||||||
|
'-r',
|
||||||
|
'--resources',
|
||||||
|
help='list of resources',
|
||||||
|
nargs='+',
|
||||||
|
required=True)
|
||||||
|
|
||||||
|
return parser.parse_args()
|
||||||
|
|
||||||
|
|
||||||
def main():
|
def main():
|
||||||
print 'Solar'
|
args = parse()
|
||||||
|
|
||||||
|
print 'ACTION %s' % args.action
|
||||||
|
print 'RESOURCES %s' % args.resources
|
||||||
|
|
||||||
|
storage = Storage.from_files('./schema/resources')
|
||||||
|
orch = ansible.AnsibleOrchestration(
|
||||||
|
[storage.get(r) for r in args.resources])
|
||||||
|
|
||||||
|
|
||||||
|
with open('tmp/hosts', 'w') as f:
|
||||||
|
f.write(orch.inventory)
|
||||||
|
|
||||||
|
with open('tmp/group_vars/all', 'w') as f:
|
||||||
|
f.write(yaml.dump(orch.vars, default_flow_style=False))
|
||||||
|
|
||||||
|
with open('tmp/main.yml', 'w') as f:
|
||||||
|
f.write(
|
||||||
|
yaml.dump(getattr(orch, args.action)(),
|
||||||
|
default_flow_style=False))
|
||||||
|
58
solar/solar/core/ansible.py
Normal file
58
solar/solar/core/ansible.py
Normal file
@ -0,0 +1,58 @@
|
|||||||
|
|
||||||
|
import yaml
|
||||||
|
|
||||||
|
from solar.extensions import resource
|
||||||
|
|
||||||
|
from jinja2 import Template
|
||||||
|
|
||||||
|
|
||||||
|
ANSIBLE_INVENTORY = """
|
||||||
|
{% for node in nodes %}
|
||||||
|
{{node.node.name}} ansible_ssh_host={{node.node.ssh_host}} ansible_ssh_port={{node.node.ssh_port}}
|
||||||
|
|
||||||
|
{% endfor %}
|
||||||
|
|
||||||
|
{% for res in resources %}
|
||||||
|
[{{ res.uid }}]
|
||||||
|
{% for node in nodes %} {{node.node.name}} {% endfor %} {% endfor %}
|
||||||
|
"""
|
||||||
|
|
||||||
|
|
||||||
|
class AnsibleOrchestration(object):
|
||||||
|
|
||||||
|
def __init__(self, resources):
|
||||||
|
self.resources = [resource(r) for r in resources
|
||||||
|
if r['id'] != 'node']
|
||||||
|
self.nodes = [resource(r) for r in resources
|
||||||
|
if r['id'] == 'node']
|
||||||
|
|
||||||
|
@property
|
||||||
|
def inventory(self):
|
||||||
|
temp = Template(ANSIBLE_INVENTORY)
|
||||||
|
node_data = [n.inventory for n in self.nodes]
|
||||||
|
return temp.render(nodes=node_data, resources=self.resources)
|
||||||
|
|
||||||
|
@property
|
||||||
|
def vars(self):
|
||||||
|
result = {}
|
||||||
|
|
||||||
|
for res in self.resources:
|
||||||
|
|
||||||
|
compiled = Template(yaml.dump(res.inventory))
|
||||||
|
compiled = yaml.load(compiled.render(**result))
|
||||||
|
|
||||||
|
result.update(compiled)
|
||||||
|
|
||||||
|
return result
|
||||||
|
|
||||||
|
def run(self, action='run'):
|
||||||
|
all_playbooks = []
|
||||||
|
|
||||||
|
for res in self.resources:
|
||||||
|
|
||||||
|
all_playbooks.extend(res.execute(action))
|
||||||
|
|
||||||
|
return all_playbooks
|
||||||
|
|
||||||
|
def remove(self):
|
||||||
|
return list(reversed(self.run(action='remove')))
|
@ -0,0 +1,6 @@
|
|||||||
|
|
||||||
|
from solar.extensions import playbook
|
||||||
|
|
||||||
|
|
||||||
|
def resource(config):
|
||||||
|
return playbook.Playbook(config)
|
@ -1,9 +1,7 @@
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
class BaseResource(object):
|
class BaseResource(object):
|
||||||
|
|
||||||
def __init__(self, config, hosts='all'):
|
def __init__(self, config):
|
||||||
"""
|
"""
|
||||||
config - data described in configuration files
|
config - data described in configuration files
|
||||||
hosts - can be overwritten if resource is inside of the role,
|
hosts - can be overwritten if resource is inside of the role,
|
||||||
@ -11,24 +9,15 @@ class BaseResource(object):
|
|||||||
"""
|
"""
|
||||||
self.config = config
|
self.config = config
|
||||||
self.uid = config['id']
|
self.uid = config['id']
|
||||||
self.hosts = hosts
|
|
||||||
|
|
||||||
def prepare(self):
|
def prepare(self):
|
||||||
"""Make some changes in database state."""
|
"""Make some changes in database state."""
|
||||||
|
|
||||||
|
@property
|
||||||
def inventory(self):
|
def inventory(self):
|
||||||
"""Return data that will be used for inventory"""
|
"""Return data that will be used for inventory"""
|
||||||
if 'parameters' in self.config:
|
return {self.uid: self.config.get('input', {})}
|
||||||
params = self.config.get('parameters', {})
|
|
||||||
|
|
||||||
res = {}
|
def execute(self, action):
|
||||||
|
|
||||||
for param, values in self.config.parameters.items():
|
|
||||||
res[param] = values.get('value') or values.get('default')
|
|
||||||
|
|
||||||
return res
|
|
||||||
|
|
||||||
|
|
||||||
def run(self):
|
|
||||||
"""Return data that will be used by orchestration framework"""
|
"""Return data that will be used by orchestration framework"""
|
||||||
raise NotImplemented('Mandatory to overwrite')
|
raise NotImplemented('Mandatory to overwrite')
|
8
solar/solar/extensions/playbook.py
Normal file
8
solar/solar/extensions/playbook.py
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
|
||||||
|
from solar.extensions import base
|
||||||
|
|
||||||
|
|
||||||
|
class Playbook(base.BaseResource):
|
||||||
|
|
||||||
|
def execute(self, action):
|
||||||
|
return self.config.get('actions', {}).get(action, [])
|
@ -0,0 +1,39 @@
|
|||||||
|
|
||||||
|
import os
|
||||||
|
from fnmatch import fnmatch
|
||||||
|
|
||||||
|
import yaml
|
||||||
|
|
||||||
|
|
||||||
|
def get_files(path, pattern):
|
||||||
|
for root, dirs, files in os.walk(path):
|
||||||
|
for file_name in files:
|
||||||
|
if fnmatch(file_name, pattern):
|
||||||
|
yield os.path.join(root, file_name)
|
||||||
|
|
||||||
|
|
||||||
|
class Storage(object):
|
||||||
|
|
||||||
|
def __init__(self):
|
||||||
|
self.entities = {}
|
||||||
|
|
||||||
|
def add(self, resource):
|
||||||
|
if 'id' in resource:
|
||||||
|
self.entities[resource['id']] = resource
|
||||||
|
|
||||||
|
def add_resource(self, resource):
|
||||||
|
if 'id' in resource:
|
||||||
|
self.entities[resource['id']] = resource
|
||||||
|
|
||||||
|
def get(self, resource_id):
|
||||||
|
return self.entities[resource_id]
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def from_files(cls, path):
|
||||||
|
store = cls()
|
||||||
|
for file_path in get_files(path, '*.yml'):
|
||||||
|
with open(file_path) as f:
|
||||||
|
entity = yaml.load(f)
|
||||||
|
|
||||||
|
store.add_resource(entity)
|
||||||
|
return store
|
@ -1,13 +0,0 @@
|
|||||||
|
|
||||||
|
|
||||||
setup(
|
|
||||||
name='tool',
|
|
||||||
version='0.1',
|
|
||||||
license='Apache License 2.0',
|
|
||||||
include_package_data=True,
|
|
||||||
install_requires=['ansible', 'networkx', 'pyyaml', 'argparse'],
|
|
||||||
entry_points="""
|
|
||||||
[console_scripts]
|
|
||||||
tool=tool.main:main
|
|
||||||
""",
|
|
||||||
)
|
|
@ -1,35 +0,0 @@
|
|||||||
|
|
||||||
import sys
|
|
||||||
|
|
||||||
import argparse
|
|
||||||
import yaml
|
|
||||||
|
|
||||||
from tool.profile_handlers import process
|
|
||||||
|
|
||||||
|
|
||||||
def parse():
|
|
||||||
|
|
||||||
parser = argparse.ArgumentParser()
|
|
||||||
|
|
||||||
parser.add_argument(
|
|
||||||
'-p',
|
|
||||||
'--profile',
|
|
||||||
help='profile file',
|
|
||||||
required=True)
|
|
||||||
|
|
||||||
parser.add_argument(
|
|
||||||
'-r',
|
|
||||||
'--resources',
|
|
||||||
help='resources dir',
|
|
||||||
required=True)
|
|
||||||
|
|
||||||
return parser.parse_args()
|
|
||||||
|
|
||||||
|
|
||||||
def main():
|
|
||||||
args = parse()
|
|
||||||
|
|
||||||
with open(args.config) as f:
|
|
||||||
profile = yaml.load(f)
|
|
||||||
|
|
||||||
return process(profile, resources)
|
|
@ -1,9 +0,0 @@
|
|||||||
|
|
||||||
|
|
||||||
from tool.profile_handlers import ansible
|
|
||||||
|
|
||||||
|
|
||||||
def process(profile, resources):
|
|
||||||
|
|
||||||
# it should be a fabric
|
|
||||||
return ansible.process(profile, resources)
|
|
@ -1,16 +0,0 @@
|
|||||||
|
|
||||||
from tool.resource_handlers import playbook
|
|
||||||
from tool.service_handlers import base
|
|
||||||
|
|
||||||
|
|
||||||
class AnsibleProfile(object):
|
|
||||||
"""This profile should just serialize
|
|
||||||
"""
|
|
||||||
|
|
||||||
def __init__(self, storage, config):
|
|
||||||
self.config = config
|
|
||||||
|
|
||||||
def inventory(self):
|
|
||||||
pass
|
|
||||||
|
|
||||||
|
|
@ -1,32 +0,0 @@
|
|||||||
|
|
||||||
"""The point of different ansible graph handlers is that graph data model
|
|
||||||
allows to decidy which tasks to run in background.
|
|
||||||
"""
|
|
||||||
|
|
||||||
|
|
||||||
import networkx as nx
|
|
||||||
|
|
||||||
|
|
||||||
class ProfileGraph(nx.DiGraph):
|
|
||||||
|
|
||||||
def __init__(self, profile):
|
|
||||||
super(ProfileGraph, self).__init__()
|
|
||||||
|
|
||||||
def add_resources(self, entity):
|
|
||||||
resources = entity.get('resources', [])
|
|
||||||
for res in resources:
|
|
||||||
self.add_resource(res)
|
|
||||||
|
|
||||||
def add_resource(self, resource):
|
|
||||||
self.add_node(resource['id'])
|
|
||||||
for dep in resource.get('requires', []):
|
|
||||||
self.add_edge(dep, resource['id'])
|
|
||||||
for dep in resource.get('required_for', []):
|
|
||||||
self.add_edge(resource['id'], dep)
|
|
||||||
|
|
||||||
|
|
||||||
def process(profile, resources):
|
|
||||||
# here we should know how to traverse profile data model
|
|
||||||
# that is specific to ansible
|
|
||||||
|
|
||||||
graph = nx.DiGraph(profile)
|
|
@ -1,20 +0,0 @@
|
|||||||
"""
|
|
||||||
This handler required for custom modification for the networks resource.
|
|
||||||
|
|
||||||
It will create all required tasks for things like ovs/linux network
|
|
||||||
entities.
|
|
||||||
"""
|
|
||||||
|
|
||||||
from tool.resoure_handlers import base
|
|
||||||
|
|
||||||
|
|
||||||
class NetworkSchema(base.BaseResource):
|
|
||||||
|
|
||||||
def __init__(self, parameters):
|
|
||||||
pass
|
|
||||||
|
|
||||||
def add_bridge(self, bridge):
|
|
||||||
return 'shell: ovs-vsctl add-br {0}'.format(bridge)
|
|
||||||
|
|
||||||
def add_port(self, bridge, port):
|
|
||||||
return 'shell: ovs-vsctl add-port {0} {1}'.format(bridge, port)
|
|
@ -1,14 +0,0 @@
|
|||||||
"""
|
|
||||||
Just find or create existing playbook.
|
|
||||||
"""
|
|
||||||
|
|
||||||
from tool.resource_handlers import base
|
|
||||||
|
|
||||||
|
|
||||||
class Playbook(base.BaseResource):
|
|
||||||
|
|
||||||
def run(self):
|
|
||||||
return {
|
|
||||||
'hosts': self.hosts,
|
|
||||||
'tasks': self.config.get('run', []),
|
|
||||||
'sudo': 'yes'}
|
|
@ -1,54 +0,0 @@
|
|||||||
|
|
||||||
|
|
||||||
import os
|
|
||||||
from fnmatch import fnmatch
|
|
||||||
|
|
||||||
import yaml
|
|
||||||
|
|
||||||
|
|
||||||
def get_files(path, pattern):
|
|
||||||
for root, dirs, files in os.walk(path):
|
|
||||||
for file_name in files:
|
|
||||||
if fnmatch(file_name, file_pattern):
|
|
||||||
yield os.path.join(root, file_name)
|
|
||||||
|
|
||||||
|
|
||||||
class Storage(object):
|
|
||||||
|
|
||||||
def __init__(self):
|
|
||||||
self.entities = {}
|
|
||||||
|
|
||||||
def add(self, resource):
|
|
||||||
if 'id' in resource:
|
|
||||||
self.entities[resource['id']] = resource
|
|
||||||
|
|
||||||
def add_profile(self, profile):
|
|
||||||
self.entities[profile['id']] = profile
|
|
||||||
for res in profile.get('resources', []):
|
|
||||||
self.add_resource(res)
|
|
||||||
|
|
||||||
def add_resource(self, resource):
|
|
||||||
if 'id' in resource:
|
|
||||||
self.entities[resource['id']] = resource
|
|
||||||
|
|
||||||
def add_service(self, service):
|
|
||||||
if 'id' in service:
|
|
||||||
self.entities[service['id']] = service
|
|
||||||
for resource in service.get('resources', []):
|
|
||||||
self.add_resource(resource)
|
|
||||||
|
|
||||||
def get(self, resource_id):
|
|
||||||
return self.entities[resource_id]
|
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def from_files(self, path):
|
|
||||||
for file_path in get_files(path, '*.yml'):
|
|
||||||
with open(file_path) as f:
|
|
||||||
entity = yaml.load(f)
|
|
||||||
|
|
||||||
if entity['type'] == 'profile':
|
|
||||||
self.add_profile(entity)
|
|
||||||
elif entity['type'] == 'resource':
|
|
||||||
self.add_resource(entity)
|
|
||||||
elif entity['type'] == 'service':
|
|
||||||
self.add_service(entity)
|
|
Loading…
x
Reference in New Issue
Block a user