Merge pull request #198 from loles/vr_update

Add updates section
This commit is contained in:
Jędrzej Nowak 2015-09-25 09:40:19 +02:00
commit c041b5974f
3 changed files with 104 additions and 50 deletions

View File

@ -20,8 +20,9 @@ import yaml
from jinja2 import Template, Environment, meta
from solar.core import provider
from solar.core import resource
from solar.core import signals
from solar.core.resource import load as load_resource
from solar.core.resource import Resource
from solar.events.api import add_event
from solar.events.controls import React, Dep
@ -57,20 +58,22 @@ def create_resource(name, base_path, args=None, virtual_resource=None):
# List args init with empty list. Elements will be added later
args = {key: (value if not isinstance(value, list) else []) for key, value in args.items()}
r = resource.Resource(
r = Resource(
name, base_path, args=args, tags=[], virtual_resource=virtual_resource
)
return r
def create_virtual_resource(vr_name, template):
template_resources = template['resources']
template_events = template.get('events', {})
template_resources = template.get('resources', [])
template_events = template.get('events', [])
resources_to_update = template.get('updates', [])
created_resources = create_resources(template_resources)
events = parse_events(template_events)
for event in events:
add_event(event)
update_resources(resources_to_update)
return created_resources
@ -113,16 +116,36 @@ def create_resources(resources):
cwd = os.getcwd()
for r in resources:
resource_name = r['id']
base_path = os.path.join(cwd, r['from'])
args = r['values']
new_resources = create(resource_name, base_path, args)
from_path = r.get('from', None)
base_path = os.path.join(cwd, from_path)
new_resources = create(resource_name, base_path)
created_resources += new_resources
if not is_virtual(base_path):
add_connections(resource_name, args)
update_inputs(resource_name, args)
return created_resources
def update_resources(resources):
for r in resources:
resource_name = r['id']
args = r['values']
update_inputs(resource_name, args)
def update_inputs(child, args):
child = load_resource(child)
connections, assignments = parse_inputs(args)
for c in connections:
mapping = {}
parent = load_resource(c['parent'])
events = c['events']
mapping[c['parent_input']] = c['child_input']
signals.connect(parent, child, mapping, events)
child.update(assignments)
def parse_events(events):
parsed_events = []
for event in events:
@ -140,38 +163,46 @@ def parse_events(events):
return parsed_events
def add_connections(resource_name, args):
def parse_inputs(args):
connections = []
for receiver_input, arg in args.items():
assignments = {}
for r_input, arg in args.items():
if isinstance(arg, list):
for item in arg:
c = parse_connection(resource_name, receiver_input, item)
connections.append(c)
c, a = parse_list_input(r_input, arg)
connections.extend(c)
assignments.update(a)
else:
c = parse_connection(resource_name, receiver_input, arg)
connections.append(c)
connections = [c for c in connections if c is not None]
for c in connections:
parent = resource.load(c['parent'])
child = resource.load(c['child'])
events = c['events']
mapping = {c['parent_input'] : c['child_input']}
signals.connect(parent, child, mapping, events)
if isinstance(arg, basestring) and '::' in arg:
c = parse_connection(r_input, arg)
connections.append(c)
else:
assignments[r_input] = arg
return connections, assignments
def parse_connection(receiver, receiver_input, element):
if isinstance(element, basestring) and '::' in element:
emitter, src = element.split('::', 1)
try:
src, events = src.split('::')
if events == 'NO_EVENTS':
events = False
except ValueError:
events = None
return {'child': receiver,
'child_input': receiver_input,
'parent' : emitter,
'parent_input': src,
'events' : events
}
def parse_list_input(r_input, args):
connections = []
assignments = {}
for arg in args:
if isinstance(arg, basestring) and '::' in arg:
c = parse_connection(r_input, arg)
connections.append(c)
else:
# Not supported yet
raise Exception('Only connections are supported in lists')
return connections, assignments
def parse_connection(child_input, element):
parent, parent_input = element.split('::', 1)
try:
parent_input, events = parent_input.split('::')
if events == 'NO_EVENTS':
events = False
except ValueError:
events = None
return {'child_input': child_input,
'parent' : parent,
'parent_input': parent_input,
'events' : events
}

View File

@ -0,0 +1,5 @@
id: simple_multinode
updates:
- id: node1
values:
ip: '10.0.0.4'

View File

@ -46,6 +46,11 @@ def bad_event_type():
'''
return yaml.load(StringIO(events))
def test_create_path_does_not_exists():
with pytest.raises(Exception) as excinfo:
vr.create('node1', '/path/does/not/exists')
err = 'Base resource does not exist: /path/does/not/exists'
assert str(excinfo.value) == err
def test_create_resource():
node_path = os.path.join(
@ -68,6 +73,26 @@ def test_create_virtual_resource(tmpdir):
resources = vr.create('nodes', str(vr_file))
assert len(resources) == 2
def test_update(tmpdir):
# XXX: make helper for it
base_path = os.path.join(
os.path.dirname(os.path.realpath(__file__)),
'resource_fixtures')
vr_node_tmpl_path = os.path.join(base_path, 'nodes.yaml.tmpl')
vr_update_tmpl_path = os.path.join(base_path, 'update.yaml.tmpl')
update_path = os.path.join(base_path, 'update')
node_resource_path = os.path.join(base_path, 'node')
with open(vr_node_tmpl_path) as f:
vr_data = f.read().format(resource_path=node_resource_path)
with open(vr_update_tmpl_path) as f:
update_data = f.read().format(resource_path=update_path)
vr_file = tmpdir.join('nodes.yaml')
vr_file.write(vr_data)
update_file = tmpdir.join('update.yaml')
update_file.write(update_data)
resources = vr.create('nodes', str(vr_file))
vr.create('updates', str(update_file))
assert resources[0].args['ip'] == '10.0.0.4'
def test_parse_events(good_events):
events =[Dep(parent='service1', parent_action='run',
@ -92,30 +117,23 @@ def test_add_connections(mocker, resources):
'servers': ['node1::ip', 'node2::ip'],
'alias': 'ser1'
}
vr.add_connections('service1', args)
vr.update_inputs('service1', args)
assert mocked_signals.connect.call_count == 3
def test_parse_connection():
correct_connection = {'child': 'host_file',
'child_input': 'ip',
correct_connection = {'child_input': 'ip',
'parent' : 'node1',
'parent_input': 'ip',
'events' : None
}
connection = vr.parse_connection('host_file', 'ip', 'node1::ip')
connection = vr.parse_connection('ip', 'node1::ip')
assert correct_connection == connection
def test_parse_connection_disable_events():
correct_connection = {'child': 'host_file',
'child_input': 'ip',
correct_connection = {'child_input': 'ip',
'parent' : 'node1',
'parent_input': 'ip',
'events' : False
}
connection = vr.parse_connection('host_file', 'ip', 'node1::ip::NO_EVENTS')
connection = vr.parse_connection('ip', 'node1::ip::NO_EVENTS')
assert correct_connection == connection
def test_parse_connection_no_connection():
connection = vr.parse_connection('host_file', 'ip', '10.0.0.2')
assert None == connection