299 lines
8.8 KiB
Python
299 lines
8.8 KiB
Python
# Copyright 2015 Mirantis, Inc.
|
|
#
|
|
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
|
# not use this file except in compliance with the License. You may obtain
|
|
# a copy of the License at
|
|
#
|
|
# http://www.apache.org/licenses/LICENSE-2.0
|
|
#
|
|
# Unless required by applicable law or agreed to in writing, software
|
|
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
|
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
|
# License for the specific language governing permissions and limitations
|
|
# under the License.
|
|
|
|
import os
|
|
|
|
from solar.core.resource import composer as cr
|
|
from solar.core import signals
|
|
from solar.events.api import add_event
|
|
from solar.events import controls
|
|
|
|
|
|
class BaseTemplate(object):
|
|
"""Base object for the template language."""
|
|
|
|
@staticmethod
|
|
def args_fmt(args, kwargs):
|
|
def fmt(v, kwargs):
|
|
if isinstance(v, basestring):
|
|
return v.format(**kwargs)
|
|
return v
|
|
|
|
return {
|
|
fmt(k, kwargs): fmt(v, kwargs) for k, v in args.items()
|
|
}
|
|
|
|
@staticmethod
|
|
def action_state_parse(action_state):
|
|
action, state = action_state.split('/')
|
|
|
|
return {
|
|
'action': action,
|
|
'state': state,
|
|
}
|
|
|
|
|
|
class ResourceTemplate(BaseTemplate):
|
|
"""Template for single resource object."""
|
|
|
|
def __init__(self, resource):
|
|
"""Initialize with solar.resource.Resource instance."""
|
|
|
|
self.resource = resource
|
|
|
|
def add_dep(self, action_state, resource, action):
|
|
"""Add single orch Dep from self to resource.
|
|
|
|
resource = resources.take(0)
|
|
resource.add_dep('run/success', some_resource, 'run')
|
|
"""
|
|
|
|
action_state = self.action_state_parse(action_state)
|
|
|
|
add_event(
|
|
controls.Dep(
|
|
self.resource.name,
|
|
action_state['action'],
|
|
action_state['state'],
|
|
resource.resource.name,
|
|
action
|
|
)
|
|
)
|
|
|
|
def add_react(self, action_state, resource, action):
|
|
"""Add single orch React from self to resource.
|
|
|
|
resource = resources.take(0)
|
|
resource.add_react('run/success', some_resource, 'run')
|
|
"""
|
|
|
|
action_state = self.action_state_parse(action_state)
|
|
|
|
add_event(
|
|
controls.React(
|
|
self.resource.name,
|
|
action_state['action'],
|
|
action_state['state'],
|
|
resource.resource.name,
|
|
action
|
|
)
|
|
)
|
|
|
|
def connect_list(self, resources, mapping=None):
|
|
"""Connect this resource to a ResourceListTemplate object.
|
|
|
|
mapping - optional connect mapping. This mapping can have the
|
|
"{receiver_num}" string which enumerates each resrouce in resources
|
|
list.
|
|
"""
|
|
mapping = mapping or {}
|
|
|
|
for receiver_num, resource in enumerate(resources.resources):
|
|
kwargs = {
|
|
'receiver_num': receiver_num,
|
|
}
|
|
|
|
mapping_fmt = self.args_fmt(mapping, kwargs)
|
|
|
|
signals.connect(self.resource, resource, mapping_fmt)
|
|
|
|
|
|
class ResourceListTemplate(BaseTemplate):
|
|
"""Template for object representing multiple resources."""
|
|
|
|
def __init__(self, resources):
|
|
"""Initialize with list of solar.resource.Resource instances."""
|
|
|
|
self.resources = resources
|
|
|
|
@classmethod
|
|
def create(cls,
|
|
count,
|
|
resource_path,
|
|
name='{resource_path_name}-{num}',
|
|
args=None):
|
|
"""Create a number of resources of the same type, with optional args.
|
|
|
|
name -- optional resource name
|
|
args -- an optional dict with create arguments. You can use
|
|
"{num}" -- index of resource in the list
|
|
"{resource_path_name}" -- name of resource from the `resource_path`
|
|
argument
|
|
"""
|
|
|
|
args = args or {}
|
|
|
|
created_resources = []
|
|
|
|
resource_path_name = os.path.split(resource_path)[-1]
|
|
|
|
for num in range(count):
|
|
kwargs = {
|
|
'num': num,
|
|
'resource_path_name': resource_path_name,
|
|
}
|
|
kwargs['name'] = name.format(**kwargs)
|
|
|
|
args_fmt = cls.args_fmt(args, kwargs)
|
|
|
|
r = cr.create(kwargs['name'],
|
|
resource_path,
|
|
args_fmt)[0]
|
|
|
|
created_resources.append(r)
|
|
|
|
return ResourceListTemplate(created_resources)
|
|
|
|
def add_dep(self, action_state, resource, action):
|
|
"""Calls add_dep for every resource in self."""
|
|
|
|
for r in self.resources:
|
|
ResourceTemplate(r).add_dep(
|
|
action_state,
|
|
resource,
|
|
action
|
|
)
|
|
|
|
def add_deps(self, action_state, resources, action):
|
|
"""Same as add_dep but adds dep for resources.
|
|
|
|
resources -- an instance of ResourceListTemplate class
|
|
"""
|
|
|
|
for r, dep_r in zip(self.resources, resources.resources):
|
|
ResourceTemplate(r).add_dep(
|
|
action_state,
|
|
ResourceTemplate(dep_r),
|
|
action
|
|
)
|
|
|
|
def add_react(self, action_state, resource, action):
|
|
"""Calls add_react for every resource in self."""
|
|
|
|
for r in self.resources:
|
|
ResourceTemplate(r).add_react(
|
|
action_state,
|
|
resource,
|
|
action
|
|
)
|
|
|
|
def add_reacts(self, action_state, resources, action):
|
|
"""Same as add_react but adds react for resources.
|
|
|
|
resources -- an instance of ResourceListTemplate class
|
|
"""
|
|
|
|
for r in resources.resources:
|
|
self.add_react(action_state, ResourceTemplate(r), action)
|
|
|
|
def filter(self, func):
|
|
"""Filter
|
|
|
|
Return ResourceListeTemplate instance with resources filtered
|
|
by func.
|
|
|
|
func -- predictate function that takes (idx, resource) as parameter
|
|
(idx is the index of resource in self.resources list)
|
|
"""
|
|
|
|
resources = filter(func, enumerate(self.resources))
|
|
|
|
return ResourceListTemplate(resources)
|
|
|
|
def connect_list(self, resources, mapping=None, events=None):
|
|
"""Connect self.resources to given resources in a 1-1 fashion.
|
|
|
|
First resource in self.resources is connected to first resource in
|
|
resources, second to second, etc.
|
|
|
|
mapping -- optional mapping
|
|
"{num}" -- substitutes for resource's index in args
|
|
"""
|
|
mapping = mapping or {}
|
|
|
|
for num, er in enumerate(zip(self.resources, resources.resources)):
|
|
emitter, receiver = er
|
|
|
|
kwargs = {
|
|
'num': num,
|
|
}
|
|
|
|
mapping_fmt = self.args_fmt(mapping, kwargs)
|
|
|
|
signals.connect(
|
|
emitter, receiver, mapping=mapping_fmt, events=events
|
|
)
|
|
|
|
def connect_list_to_each(self, resources, mapping=None, events=None):
|
|
"""Connect resources
|
|
|
|
Connect each resource in self.resources to each resource in
|
|
resources.
|
|
|
|
mapping -- optional mapping
|
|
"{emitter_num}" -- substitutes for emitter's index in mapping (from
|
|
self.resources)
|
|
"{receiver_num}" -- substitutes for receiver's index in mapping (from
|
|
resources argument)
|
|
"""
|
|
mapping = mapping or {}
|
|
|
|
for emitter_num, emitter in enumerate(self.resources):
|
|
for receiver_num, receiver in enumerate(resources.resources):
|
|
kwargs = {
|
|
'emitter_num': emitter_num,
|
|
'receiver_num': receiver_num,
|
|
}
|
|
|
|
mapping_fmt = self.args_fmt(mapping, kwargs)
|
|
|
|
signals.connect(
|
|
emitter,
|
|
receiver,
|
|
mapping=mapping_fmt,
|
|
events=events
|
|
)
|
|
|
|
def on_each(self, resource_path, args=None):
|
|
"""Create resource form resource_path on each resource."""
|
|
args = args or {}
|
|
|
|
created_resources = ResourceListTemplate.create(
|
|
len(self.resources),
|
|
resource_path,
|
|
args=args
|
|
)
|
|
|
|
for i, resource in enumerate(self.resources):
|
|
signals.connect(resource, created_resources.resources[i])
|
|
|
|
return created_resources
|
|
|
|
def take(self, i):
|
|
"""Return ResourceTemplate from self.resources[i]."""
|
|
|
|
return ResourceTemplate(self.resources[i])
|
|
|
|
def tail(self):
|
|
"""Return ResourceListTemplate form self.resources[1:]."""
|
|
|
|
return ResourceListTemplate(self.resources[1:])
|
|
|
|
|
|
def nodes_from(template_path):
|
|
"""Return ResourceListTemplate for nodes read from template_path."""
|
|
|
|
nodes = cr.create('nodes', template_path, {})
|
|
return ResourceListTemplate(nodes)
|