commit
c041b5974f
@ -20,8 +20,9 @@ import yaml
|
|||||||
from jinja2 import Template, Environment, meta
|
from jinja2 import Template, Environment, meta
|
||||||
|
|
||||||
from solar.core import provider
|
from solar.core import provider
|
||||||
from solar.core import resource
|
|
||||||
from solar.core import signals
|
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.api import add_event
|
||||||
from solar.events.controls import React, Dep
|
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
|
# 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()}
|
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
|
name, base_path, args=args, tags=[], virtual_resource=virtual_resource
|
||||||
)
|
)
|
||||||
return r
|
return r
|
||||||
|
|
||||||
|
|
||||||
def create_virtual_resource(vr_name, template):
|
def create_virtual_resource(vr_name, template):
|
||||||
template_resources = template['resources']
|
template_resources = template.get('resources', [])
|
||||||
template_events = template.get('events', {})
|
template_events = template.get('events', [])
|
||||||
|
resources_to_update = template.get('updates', [])
|
||||||
|
|
||||||
created_resources = create_resources(template_resources)
|
created_resources = create_resources(template_resources)
|
||||||
events = parse_events(template_events)
|
events = parse_events(template_events)
|
||||||
for event in events:
|
for event in events:
|
||||||
add_event(event)
|
add_event(event)
|
||||||
|
update_resources(resources_to_update)
|
||||||
return created_resources
|
return created_resources
|
||||||
|
|
||||||
|
|
||||||
@ -113,16 +116,36 @@ def create_resources(resources):
|
|||||||
cwd = os.getcwd()
|
cwd = os.getcwd()
|
||||||
for r in resources:
|
for r in resources:
|
||||||
resource_name = r['id']
|
resource_name = r['id']
|
||||||
base_path = os.path.join(cwd, r['from'])
|
|
||||||
args = r['values']
|
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
|
created_resources += new_resources
|
||||||
|
|
||||||
if not is_virtual(base_path):
|
if not is_virtual(base_path):
|
||||||
add_connections(resource_name, args)
|
update_inputs(resource_name, args)
|
||||||
return created_resources
|
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):
|
def parse_events(events):
|
||||||
parsed_events = []
|
parsed_events = []
|
||||||
for event in events:
|
for event in events:
|
||||||
@ -140,38 +163,46 @@ def parse_events(events):
|
|||||||
return parsed_events
|
return parsed_events
|
||||||
|
|
||||||
|
|
||||||
def add_connections(resource_name, args):
|
def parse_inputs(args):
|
||||||
connections = []
|
connections = []
|
||||||
for receiver_input, arg in args.items():
|
assignments = {}
|
||||||
|
for r_input, arg in args.items():
|
||||||
if isinstance(arg, list):
|
if isinstance(arg, list):
|
||||||
for item in arg:
|
c, a = parse_list_input(r_input, arg)
|
||||||
c = parse_connection(resource_name, receiver_input, item)
|
connections.extend(c)
|
||||||
|
assignments.update(a)
|
||||||
|
else:
|
||||||
|
if isinstance(arg, basestring) and '::' in arg:
|
||||||
|
c = parse_connection(r_input, arg)
|
||||||
connections.append(c)
|
connections.append(c)
|
||||||
else:
|
else:
|
||||||
c = parse_connection(resource_name, receiver_input, arg)
|
assignments[r_input] = arg
|
||||||
|
return connections, assignments
|
||||||
|
|
||||||
|
|
||||||
|
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)
|
connections.append(c)
|
||||||
|
else:
|
||||||
connections = [c for c in connections if c is not None]
|
# Not supported yet
|
||||||
for c in connections:
|
raise Exception('Only connections are supported in lists')
|
||||||
parent = resource.load(c['parent'])
|
return connections, assignments
|
||||||
child = resource.load(c['child'])
|
|
||||||
events = c['events']
|
|
||||||
mapping = {c['parent_input'] : c['child_input']}
|
|
||||||
signals.connect(parent, child, mapping, events)
|
|
||||||
|
|
||||||
|
|
||||||
def parse_connection(receiver, receiver_input, element):
|
def parse_connection(child_input, element):
|
||||||
if isinstance(element, basestring) and '::' in element:
|
parent, parent_input = element.split('::', 1)
|
||||||
emitter, src = element.split('::', 1)
|
|
||||||
try:
|
try:
|
||||||
src, events = src.split('::')
|
parent_input, events = parent_input.split('::')
|
||||||
if events == 'NO_EVENTS':
|
if events == 'NO_EVENTS':
|
||||||
events = False
|
events = False
|
||||||
except ValueError:
|
except ValueError:
|
||||||
events = None
|
events = None
|
||||||
return {'child': receiver,
|
return {'child_input': child_input,
|
||||||
'child_input': receiver_input,
|
'parent' : parent,
|
||||||
'parent' : emitter,
|
'parent_input': parent_input,
|
||||||
'parent_input': src,
|
|
||||||
'events' : events
|
'events' : events
|
||||||
}
|
}
|
||||||
|
5
solar/solar/test/resource_fixtures/update.yaml.tmpl
Normal file
5
solar/solar/test/resource_fixtures/update.yaml.tmpl
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
id: simple_multinode
|
||||||
|
updates:
|
||||||
|
- id: node1
|
||||||
|
values:
|
||||||
|
ip: '10.0.0.4'
|
@ -46,6 +46,11 @@ def bad_event_type():
|
|||||||
'''
|
'''
|
||||||
return yaml.load(StringIO(events))
|
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():
|
def test_create_resource():
|
||||||
node_path = os.path.join(
|
node_path = os.path.join(
|
||||||
@ -68,6 +73,26 @@ def test_create_virtual_resource(tmpdir):
|
|||||||
resources = vr.create('nodes', str(vr_file))
|
resources = vr.create('nodes', str(vr_file))
|
||||||
assert len(resources) == 2
|
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):
|
def test_parse_events(good_events):
|
||||||
events =[Dep(parent='service1', parent_action='run',
|
events =[Dep(parent='service1', parent_action='run',
|
||||||
@ -92,30 +117,23 @@ def test_add_connections(mocker, resources):
|
|||||||
'servers': ['node1::ip', 'node2::ip'],
|
'servers': ['node1::ip', 'node2::ip'],
|
||||||
'alias': 'ser1'
|
'alias': 'ser1'
|
||||||
}
|
}
|
||||||
vr.add_connections('service1', args)
|
vr.update_inputs('service1', args)
|
||||||
assert mocked_signals.connect.call_count == 3
|
assert mocked_signals.connect.call_count == 3
|
||||||
|
|
||||||
|
|
||||||
def test_parse_connection():
|
def test_parse_connection():
|
||||||
correct_connection = {'child': 'host_file',
|
correct_connection = {'child_input': 'ip',
|
||||||
'child_input': 'ip',
|
|
||||||
'parent' : 'node1',
|
'parent' : 'node1',
|
||||||
'parent_input': 'ip',
|
'parent_input': 'ip',
|
||||||
'events' : None
|
'events' : None
|
||||||
}
|
}
|
||||||
connection = vr.parse_connection('host_file', 'ip', 'node1::ip')
|
connection = vr.parse_connection('ip', 'node1::ip')
|
||||||
assert correct_connection == connection
|
assert correct_connection == connection
|
||||||
|
|
||||||
def test_parse_connection_disable_events():
|
def test_parse_connection_disable_events():
|
||||||
correct_connection = {'child': 'host_file',
|
correct_connection = {'child_input': 'ip',
|
||||||
'child_input': 'ip',
|
|
||||||
'parent' : 'node1',
|
'parent' : 'node1',
|
||||||
'parent_input': 'ip',
|
'parent_input': 'ip',
|
||||||
'events' : False
|
'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
|
assert correct_connection == connection
|
||||||
|
|
||||||
def test_parse_connection_no_connection():
|
|
||||||
connection = vr.parse_connection('host_file', 'ip', '10.0.0.2')
|
|
||||||
assert None == connection
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user