Remove old resource/signals/observers stuff
This commit is contained in:
		@@ -1,222 +0,0 @@
 | 
			
		||||
#    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.
 | 
			
		||||
 | 
			
		||||
from solar.core.log import log
 | 
			
		||||
from solar.core import signals
 | 
			
		||||
from solar.interfaces.db import get_db
 | 
			
		||||
 | 
			
		||||
db = get_db()
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class BaseObserver(object):
 | 
			
		||||
    type_ = None
 | 
			
		||||
 | 
			
		||||
    def __init__(self, attached_to, name, value):
 | 
			
		||||
        """
 | 
			
		||||
        :param attached_to: resource.Resource
 | 
			
		||||
        :param name:
 | 
			
		||||
        :param value:
 | 
			
		||||
        :return:
 | 
			
		||||
        """
 | 
			
		||||
        self._attached_to_name = attached_to.name
 | 
			
		||||
        self.name = name
 | 
			
		||||
        self.value = value
 | 
			
		||||
 | 
			
		||||
    @property
 | 
			
		||||
    def attached_to(self):
 | 
			
		||||
        from solar.core import resource
 | 
			
		||||
 | 
			
		||||
        return resource.load(self._attached_to_name)
 | 
			
		||||
 | 
			
		||||
    @property
 | 
			
		||||
    def receivers(self):
 | 
			
		||||
        from solar.core import resource
 | 
			
		||||
 | 
			
		||||
        for receiver_name, receiver_input in signals.Connections.receivers(
 | 
			
		||||
                    self._attached_to_name,
 | 
			
		||||
                    self.name
 | 
			
		||||
                ):
 | 
			
		||||
            yield resource.load(receiver_name).args[receiver_input]
 | 
			
		||||
 | 
			
		||||
    def __repr__(self):
 | 
			
		||||
        return '[{}:{}] {}'.format(self._attached_to_name, self.name, self.value)
 | 
			
		||||
 | 
			
		||||
    def __unicode__(self):
 | 
			
		||||
        return unicode(self.value)
 | 
			
		||||
 | 
			
		||||
    def __eq__(self, other):
 | 
			
		||||
        if isinstance(other, BaseObserver):
 | 
			
		||||
            return self.value == other.value
 | 
			
		||||
 | 
			
		||||
        return self.value == other
 | 
			
		||||
 | 
			
		||||
    def notify(self, emitter):
 | 
			
		||||
        """
 | 
			
		||||
        :param emitter: Observer
 | 
			
		||||
        :return:
 | 
			
		||||
        """
 | 
			
		||||
        raise NotImplementedError
 | 
			
		||||
 | 
			
		||||
    def update(self, value):
 | 
			
		||||
        """
 | 
			
		||||
        :param value:
 | 
			
		||||
        :return:
 | 
			
		||||
        """
 | 
			
		||||
        raise NotImplementedError
 | 
			
		||||
 | 
			
		||||
    def find_receiver(self, receiver):
 | 
			
		||||
        fltr = [r for r in self.receivers
 | 
			
		||||
                if r._attached_to_name == receiver._attached_to_name
 | 
			
		||||
                and r.name == receiver.name]
 | 
			
		||||
        if fltr:
 | 
			
		||||
            return fltr[0]
 | 
			
		||||
 | 
			
		||||
    def subscribe(self, receiver):
 | 
			
		||||
        """
 | 
			
		||||
        :param receiver: Observer
 | 
			
		||||
        :return:
 | 
			
		||||
        """
 | 
			
		||||
        log.debug('Subscribe %s', receiver)
 | 
			
		||||
        # No multiple subscriptions
 | 
			
		||||
        if self.find_receiver(receiver):
 | 
			
		||||
            log.error('No multiple subscriptions from %s', receiver)
 | 
			
		||||
            return
 | 
			
		||||
        receiver.subscribed(self)
 | 
			
		||||
 | 
			
		||||
        signals.Connections.add(
 | 
			
		||||
            self.attached_to,
 | 
			
		||||
            self.name,
 | 
			
		||||
            receiver.attached_to,
 | 
			
		||||
            receiver.name
 | 
			
		||||
        )
 | 
			
		||||
 | 
			
		||||
        receiver.notify(self)
 | 
			
		||||
 | 
			
		||||
    def subscribed(self, emitter):
 | 
			
		||||
        log.debug('Subscribed %s', emitter)
 | 
			
		||||
 | 
			
		||||
    def unsubscribe(self, receiver):
 | 
			
		||||
        """
 | 
			
		||||
        :param receiver: Observer
 | 
			
		||||
        :return:
 | 
			
		||||
        """
 | 
			
		||||
        log.debug('Unsubscribe %s', receiver)
 | 
			
		||||
        if self.find_receiver(receiver):
 | 
			
		||||
            receiver.unsubscribed(self)
 | 
			
		||||
 | 
			
		||||
        signals.Connections.remove(
 | 
			
		||||
            self.attached_to,
 | 
			
		||||
            self.name,
 | 
			
		||||
            receiver.attached_to,
 | 
			
		||||
            receiver.name
 | 
			
		||||
        )
 | 
			
		||||
 | 
			
		||||
        # TODO: ?
 | 
			
		||||
        #receiver.notify(self)
 | 
			
		||||
 | 
			
		||||
    def unsubscribed(self, emitter):
 | 
			
		||||
        log.debug('Unsubscribed %s', emitter)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class Observer(BaseObserver):
 | 
			
		||||
    type_ = 'simple'
 | 
			
		||||
 | 
			
		||||
    @property
 | 
			
		||||
    def emitter(self):
 | 
			
		||||
        from solar.core import resource
 | 
			
		||||
 | 
			
		||||
        emitter = signals.Connections.emitter(self._attached_to_name, self.name)
 | 
			
		||||
 | 
			
		||||
        if emitter is not None:
 | 
			
		||||
            emitter_name, emitter_input_name = emitter
 | 
			
		||||
            return resource.load(emitter_name).args[emitter_input_name]
 | 
			
		||||
 | 
			
		||||
    def notify(self, emitter):
 | 
			
		||||
        log.debug('Notify from %s value %s', emitter, emitter.value)
 | 
			
		||||
        # Copy emitter's values to receiver
 | 
			
		||||
        self.value = emitter.value
 | 
			
		||||
        for receiver in self.receivers:
 | 
			
		||||
            receiver.notify(self)
 | 
			
		||||
        self.attached_to.set_args_from_dict({self.name: self.value})
 | 
			
		||||
 | 
			
		||||
    def update(self, value):
 | 
			
		||||
        log.debug('Updating to value %s', value)
 | 
			
		||||
        self.value = value
 | 
			
		||||
        for receiver in self.receivers:
 | 
			
		||||
            receiver.notify(self)
 | 
			
		||||
        self.attached_to.set_args_from_dict({self.name: self.value})
 | 
			
		||||
 | 
			
		||||
    def subscribed(self, emitter):
 | 
			
		||||
        super(Observer, self).subscribed(emitter)
 | 
			
		||||
        # Simple observer can be attached to at most one emitter
 | 
			
		||||
        if self.emitter is not None:
 | 
			
		||||
            self.emitter.unsubscribe(self)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class ListObserver(BaseObserver):
 | 
			
		||||
    type_ = 'list'
 | 
			
		||||
 | 
			
		||||
    def __unicode__(self):
 | 
			
		||||
        return unicode(self.value)
 | 
			
		||||
 | 
			
		||||
    @staticmethod
 | 
			
		||||
    def _format_value(emitter):
 | 
			
		||||
        return {
 | 
			
		||||
            'emitter': emitter.name,
 | 
			
		||||
            'emitter_attached_to': emitter._attached_to_name,
 | 
			
		||||
            'value': emitter.value,
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
    def notify(self, emitter):
 | 
			
		||||
        log.debug('Notify from %s value %s', emitter, emitter.value)
 | 
			
		||||
        # Copy emitter's values to receiver
 | 
			
		||||
        idx = self._emitter_idx(emitter)
 | 
			
		||||
        self.value[idx] = self._format_value(emitter)
 | 
			
		||||
        for receiver in self.receivers:
 | 
			
		||||
            receiver.notify(self)
 | 
			
		||||
        self.attached_to.set_args_from_dict({self.name: self.value})
 | 
			
		||||
 | 
			
		||||
    def subscribed(self, emitter):
 | 
			
		||||
        super(ListObserver, self).subscribed(emitter)
 | 
			
		||||
        idx = self._emitter_idx(emitter)
 | 
			
		||||
        if idx is None:
 | 
			
		||||
            self.value.append(self._format_value(emitter))
 | 
			
		||||
        self.attached_to.set_args_from_dict({self.name: self.value})
 | 
			
		||||
 | 
			
		||||
    def unsubscribed(self, emitter):
 | 
			
		||||
        """
 | 
			
		||||
        :param receiver: Observer
 | 
			
		||||
        :return:
 | 
			
		||||
        """
 | 
			
		||||
        log.debug('Unsubscribed emitter %s', emitter)
 | 
			
		||||
        idx = self._emitter_idx(emitter)
 | 
			
		||||
        self.value.pop(idx)
 | 
			
		||||
        self.attached_to.set_args_from_dict({self.name: self.value})
 | 
			
		||||
        for receiver in self.receivers:
 | 
			
		||||
            receiver.notify(self)
 | 
			
		||||
 | 
			
		||||
    def _emitter_idx(self, emitter):
 | 
			
		||||
        try:
 | 
			
		||||
            return [i for i, e in enumerate(self.value)
 | 
			
		||||
                    if e['emitter_attached_to'] == emitter._attached_to_name
 | 
			
		||||
                    ][0]
 | 
			
		||||
        except IndexError:
 | 
			
		||||
            return
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def create(type_, *args, **kwargs):
 | 
			
		||||
    for klass in BaseObserver.__subclasses__():
 | 
			
		||||
        if klass.type_ == type_:
 | 
			
		||||
            return klass(*args, **kwargs)
 | 
			
		||||
    raise NotImplementedError('No handling class for type {}'.format(type_))
 | 
			
		||||
@@ -1,22 +0,0 @@
 | 
			
		||||
__all__ = [
 | 
			
		||||
    'Resource',
 | 
			
		||||
    'assign_resources_to_nodes',
 | 
			
		||||
    'connect_resources',
 | 
			
		||||
    'create',
 | 
			
		||||
    'load',
 | 
			
		||||
    'load_all',
 | 
			
		||||
    'prepare_meta',
 | 
			
		||||
    'wrap_resource',
 | 
			
		||||
    'validate_resources',
 | 
			
		||||
]
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
from solar.core.resource.resource import Resource
 | 
			
		||||
from solar.core.resource.resource import assign_resources_to_nodes
 | 
			
		||||
from solar.core.resource.resource import connect_resources
 | 
			
		||||
from solar.core.resource.resource import load
 | 
			
		||||
from solar.core.resource.resource import load_all
 | 
			
		||||
from solar.core.resource.resource import wrap_resource
 | 
			
		||||
from solar.core.resource.virtual_resource import create
 | 
			
		||||
from solar.core.resource.virtual_resource import prepare_meta
 | 
			
		||||
from solar.core.resource.virtual_resource import validate_resources
 | 
			
		||||
@@ -1,80 +0,0 @@
 | 
			
		||||
#    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 inflection
 | 
			
		||||
import os
 | 
			
		||||
import pprint
 | 
			
		||||
 | 
			
		||||
from solar.core import resource
 | 
			
		||||
from solar import utils
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
RESOURCE_HEADER_TEMPLATE = """
 | 
			
		||||
from solar.core.resource import Resource
 | 
			
		||||
"""
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
RESOURCE_CLASS_TEMPLATE = """
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class {class_name}(Resource):
 | 
			
		||||
    _metadata = {{
 | 
			
		||||
        'actions': {meta_actions},
 | 
			
		||||
        'actions_path': '{actions_path}',
 | 
			
		||||
        'base_path': '{base_path}',
 | 
			
		||||
        'input': {meta_input},
 | 
			
		||||
        'handler': '{handler}',
 | 
			
		||||
    }}
 | 
			
		||||
 | 
			
		||||
    {input_properties}
 | 
			
		||||
"""
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
RESOURCE_INPUT_PROPERTY_TEMPLATE = """
 | 
			
		||||
    @property
 | 
			
		||||
    def {name}(self):
 | 
			
		||||
        return self.args['{name}']
 | 
			
		||||
 | 
			
		||||
    @{name}.setter
 | 
			
		||||
    def {name}(self, value):
 | 
			
		||||
        #self.args['{name}'].value = value
 | 
			
		||||
        #self.set_args_from_dict({{'{name}': value}})
 | 
			
		||||
        self.update({{'{name}': value}})
 | 
			
		||||
"""
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def compile(meta):
 | 
			
		||||
    destination_file = utils.read_config()['resources-compiled-file']
 | 
			
		||||
 | 
			
		||||
    resource.prepare_meta(meta)
 | 
			
		||||
    meta['class_name'] = '{}Resource'.format(
 | 
			
		||||
        inflection.camelize(meta['base_name'])
 | 
			
		||||
    )
 | 
			
		||||
    meta['meta_actions'] = pprint.pformat(meta['actions'])
 | 
			
		||||
    meta['meta_input'] = pprint.pformat(meta['input'])
 | 
			
		||||
 | 
			
		||||
    print meta['base_name'], meta['class_name']
 | 
			
		||||
 | 
			
		||||
    if not os.path.exists(destination_file):
 | 
			
		||||
        with open(destination_file, 'w') as f:
 | 
			
		||||
            f.write(RESOURCE_HEADER_TEMPLATE.format(**meta))
 | 
			
		||||
 | 
			
		||||
    with open(destination_file, 'a') as f:
 | 
			
		||||
        input_properties = '\n'.join(
 | 
			
		||||
            RESOURCE_INPUT_PROPERTY_TEMPLATE.format(name=name)
 | 
			
		||||
            for name in meta['input']
 | 
			
		||||
        )
 | 
			
		||||
        f.write(RESOURCE_CLASS_TEMPLATE.format(
 | 
			
		||||
            input_properties=input_properties, **meta)
 | 
			
		||||
        )
 | 
			
		||||
@@ -1,237 +0,0 @@
 | 
			
		||||
# -*- coding: utf-8 -*-
 | 
			
		||||
import os
 | 
			
		||||
 | 
			
		||||
from copy import deepcopy
 | 
			
		||||
 | 
			
		||||
import solar
 | 
			
		||||
 | 
			
		||||
from solar.core import actions
 | 
			
		||||
from solar.core import observer
 | 
			
		||||
from solar.core import signals
 | 
			
		||||
from solar.core import validation
 | 
			
		||||
 | 
			
		||||
from solar.core.connections import ResourcesConnectionGraph
 | 
			
		||||
from solar.interfaces.db import get_db
 | 
			
		||||
 | 
			
		||||
db = get_db()
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class Resource(object):
 | 
			
		||||
    _metadata = {}
 | 
			
		||||
 | 
			
		||||
    def __init__(self, name, metadata, args, tags=None, virtual_resource=None):
 | 
			
		||||
        self.name = name
 | 
			
		||||
        if metadata:
 | 
			
		||||
            self.metadata = metadata
 | 
			
		||||
        else:
 | 
			
		||||
            self.metadata = deepcopy(self._metadata)
 | 
			
		||||
 | 
			
		||||
        self.metadata['id'] = name
 | 
			
		||||
 | 
			
		||||
        self.tags = tags or []
 | 
			
		||||
        self.virtual_resource = virtual_resource
 | 
			
		||||
        self.set_args_from_dict(args)
 | 
			
		||||
 | 
			
		||||
    @property
 | 
			
		||||
    def actions(self):
 | 
			
		||||
        return self.metadata.get('actions') or []
 | 
			
		||||
 | 
			
		||||
    @property
 | 
			
		||||
    def args(self):
 | 
			
		||||
        ret = {}
 | 
			
		||||
 | 
			
		||||
        args = self.args_dict()
 | 
			
		||||
 | 
			
		||||
        for arg_name, metadata_arg in self.metadata['input'].items():
 | 
			
		||||
            type_ = validation.schema_input_type(metadata_arg.get('schema', 'str'))
 | 
			
		||||
 | 
			
		||||
            ret[arg_name] = observer.create(
 | 
			
		||||
                type_, self, arg_name, args.get(arg_name)
 | 
			
		||||
            )
 | 
			
		||||
 | 
			
		||||
        return ret
 | 
			
		||||
 | 
			
		||||
    def args_dict(self):
 | 
			
		||||
        raw_resource = db.read(self.name, collection=db.COLLECTIONS.resource)
 | 
			
		||||
        if raw_resource is None:
 | 
			
		||||
            return {}
 | 
			
		||||
 | 
			
		||||
        self.metadata = raw_resource
 | 
			
		||||
 | 
			
		||||
        return Resource.get_raw_resource_args(raw_resource)
 | 
			
		||||
 | 
			
		||||
    def set_args_from_dict(self, new_args):
 | 
			
		||||
        args = self.args_dict()
 | 
			
		||||
        args.update(new_args)
 | 
			
		||||
 | 
			
		||||
        self.metadata['tags'] = self.tags
 | 
			
		||||
        self.metadata['virtual_resource'] = self.virtual_resource
 | 
			
		||||
        for k, v in args.items():
 | 
			
		||||
            if k not in self.metadata['input']:
 | 
			
		||||
                raise NotImplementedError(
 | 
			
		||||
                    'Argument {} not implemented for resource {}'.format(k, self)
 | 
			
		||||
                )
 | 
			
		||||
 | 
			
		||||
            if isinstance(v, dict) and 'value' in v:
 | 
			
		||||
                v = v['value']
 | 
			
		||||
            self.metadata['input'][k]['value'] = v
 | 
			
		||||
 | 
			
		||||
        db.save(self.name, self.metadata, collection=db.COLLECTIONS.resource)
 | 
			
		||||
 | 
			
		||||
    def set_args(self, args):
 | 
			
		||||
        self.set_args_from_dict({k: v.value for k, v in args.items()})
 | 
			
		||||
 | 
			
		||||
    def __repr__(self):
 | 
			
		||||
        return ("Resource(name='{id}', metadata={metadata}, args={input}, "
 | 
			
		||||
                "tags={tags})").format(**self.to_dict())
 | 
			
		||||
 | 
			
		||||
    def color_repr(self):
 | 
			
		||||
        import click
 | 
			
		||||
 | 
			
		||||
        arg_color = 'yellow'
 | 
			
		||||
 | 
			
		||||
        return ("{resource_s}({name_s}='{id}', {metadata_s}={metadata}, "
 | 
			
		||||
                "{args_s}={input}, {tags_s}={tags})").format(
 | 
			
		||||
            resource_s=click.style('Resource', fg='white', bold=True),
 | 
			
		||||
            name_s=click.style('name', fg=arg_color, bold=True),
 | 
			
		||||
            metadata_s=click.style('metadata', fg=arg_color, bold=True),
 | 
			
		||||
            args_s=click.style('args', fg=arg_color, bold=True),
 | 
			
		||||
            tags_s=click.style('tags', fg=arg_color, bold=True),
 | 
			
		||||
            **self.to_dict()
 | 
			
		||||
        )
 | 
			
		||||
 | 
			
		||||
    def to_dict(self):
 | 
			
		||||
        return {
 | 
			
		||||
            'id': self.name,
 | 
			
		||||
            'metadata': self.metadata,
 | 
			
		||||
            'input': self.args_show(),
 | 
			
		||||
            'tags': self.tags,
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
    def args_show(self):
 | 
			
		||||
        def formatter(v):
 | 
			
		||||
            if isinstance(v, observer.ListObserver):
 | 
			
		||||
                return v.value
 | 
			
		||||
            elif isinstance(v, observer.Observer):
 | 
			
		||||
                return {
 | 
			
		||||
                    'emitter': v.emitter.attached_to.name if v.emitter else None,
 | 
			
		||||
                    'value': v.value,
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
            return v
 | 
			
		||||
 | 
			
		||||
        return {k: formatter(v) for k, v in self.args.items()}
 | 
			
		||||
 | 
			
		||||
    def add_tag(self, tag):
 | 
			
		||||
        if tag not in self.tags:
 | 
			
		||||
            self.tags.append(tag)
 | 
			
		||||
 | 
			
		||||
    def remove_tag(self, tag):
 | 
			
		||||
        try:
 | 
			
		||||
            self.tags.remove(tag)
 | 
			
		||||
        except ValueError:
 | 
			
		||||
            pass
 | 
			
		||||
 | 
			
		||||
    def notify(self, emitter):
 | 
			
		||||
        """Update resource's args from emitter's args.
 | 
			
		||||
 | 
			
		||||
        :param emitter: Resource
 | 
			
		||||
        :return:
 | 
			
		||||
        """
 | 
			
		||||
        r_args = self.args
 | 
			
		||||
 | 
			
		||||
        for key, value in emitter.args.iteritems():
 | 
			
		||||
            r_args[key].notify(value)
 | 
			
		||||
 | 
			
		||||
    def update(self, args):
 | 
			
		||||
        """This method updates resource's args with a simple dict.
 | 
			
		||||
 | 
			
		||||
        :param args:
 | 
			
		||||
        :return:
 | 
			
		||||
        """
 | 
			
		||||
        # Update will be blocked if this resource is listening
 | 
			
		||||
        # on some input that is to be updated -- we should only listen
 | 
			
		||||
        # to the emitter and not be able to change the input's value
 | 
			
		||||
        r_args = self.args
 | 
			
		||||
 | 
			
		||||
        for key, value in args.iteritems():
 | 
			
		||||
            r_args[key].update(value)
 | 
			
		||||
 | 
			
		||||
        self.set_args(r_args)
 | 
			
		||||
 | 
			
		||||
    def action(self, action):
 | 
			
		||||
        if action in self.actions:
 | 
			
		||||
            actions.resource_action(self, action)
 | 
			
		||||
        else:
 | 
			
		||||
            raise Exception('Uuups, action is not available')
 | 
			
		||||
 | 
			
		||||
    @staticmethod
 | 
			
		||||
    def get_raw_resource_args(raw_resource):
 | 
			
		||||
        return {k: v.get('value') for k, v in raw_resource['input'].items()}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def wrap_resource(raw_resource):
 | 
			
		||||
    name = raw_resource['id']
 | 
			
		||||
    args = Resource.get_raw_resource_args(raw_resource)
 | 
			
		||||
    tags = raw_resource.get('tags', [])
 | 
			
		||||
    virtual_resource = raw_resource.get('virtual_resource', [])
 | 
			
		||||
 | 
			
		||||
    return Resource(name, raw_resource, args, tags=tags, virtual_resource=virtual_resource)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def wrap_resource_no_value(raw_resource):
 | 
			
		||||
    name = raw_resource['id']
 | 
			
		||||
    args = {k: v for k, v in raw_resource['input'].items()}
 | 
			
		||||
    tags = raw_resource.get('tags', [])
 | 
			
		||||
    virtual_resource = raw_resource.get('virtual_resource', [])
 | 
			
		||||
 | 
			
		||||
    return Resource(name, raw_resource, args, tags=tags, virtual_resource=virtual_resource)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def load(resource_name):
 | 
			
		||||
    raw_resource = db.read(resource_name, collection=db.COLLECTIONS.resource)
 | 
			
		||||
 | 
			
		||||
    if raw_resource is None:
 | 
			
		||||
        raise KeyError(
 | 
			
		||||
            'Resource {} does not exist'.format(resource_name)
 | 
			
		||||
        )
 | 
			
		||||
 | 
			
		||||
    return wrap_resource(raw_resource)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def load_all():
 | 
			
		||||
    ret = {}
 | 
			
		||||
 | 
			
		||||
    for raw_resource in db.get_list(collection=db.COLLECTIONS.resource):
 | 
			
		||||
        resource = wrap_resource(raw_resource)
 | 
			
		||||
        ret[resource.name] = resource
 | 
			
		||||
 | 
			
		||||
    return ret
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def assign_resources_to_nodes(resources, nodes):
 | 
			
		||||
    for node in nodes:
 | 
			
		||||
        for resource in resources:
 | 
			
		||||
            res = deepcopy(resource)
 | 
			
		||||
            res['tags'] = list(set(node.get('tags', [])) |
 | 
			
		||||
                               set(resource.get('tags', [])))
 | 
			
		||||
            resource_uuid = '{0}-{1}'.format(res['id'], solar.utils.generate_uuid())
 | 
			
		||||
            # We should not generate here any uuid's, because
 | 
			
		||||
            # a single node should be represented with a single
 | 
			
		||||
            # resource
 | 
			
		||||
            node_uuid = node['id']
 | 
			
		||||
 | 
			
		||||
            node_resource_template = solar.utils.read_config()['node_resource_template']
 | 
			
		||||
            args = {k: v['value'] for k, v in res['input'].items()}
 | 
			
		||||
            created_resource = create(resource_uuid, resource['dir_path'], args, tags=res['tags'])
 | 
			
		||||
            created_node = create(node_uuid, node_resource_template, node, tags=node.get('tags', []))
 | 
			
		||||
 | 
			
		||||
            signals.connect(created_node, created_resource)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def connect_resources(profile):
 | 
			
		||||
    connections = profile.get('connections', [])
 | 
			
		||||
    graph = ResourcesConnectionGraph(connections, load_all().values())
 | 
			
		||||
 | 
			
		||||
    for connection in graph.iter_connections():
 | 
			
		||||
        signals.connect(connection['from'], connection['to'], connection['mapping'])
 | 
			
		||||
@@ -1,196 +0,0 @@
 | 
			
		||||
# -*- coding: UTF-8 -*-
 | 
			
		||||
import os
 | 
			
		||||
from StringIO import StringIO
 | 
			
		||||
 | 
			
		||||
import yaml
 | 
			
		||||
 | 
			
		||||
from jinja2 import Template, Environment, meta
 | 
			
		||||
 | 
			
		||||
from solar import utils
 | 
			
		||||
from solar.core import validation
 | 
			
		||||
from solar.core.resource import load_all, Resource
 | 
			
		||||
from solar.core import provider
 | 
			
		||||
from solar.core import signals
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def create_resource(name, base_path, args, virtual_resource=None):
 | 
			
		||||
    if isinstance(base_path, provider.BaseProvider):
 | 
			
		||||
        base_path = base_path.directory
 | 
			
		||||
 | 
			
		||||
    base_meta_file = os.path.join(base_path, 'meta.yaml')
 | 
			
		||||
    actions_path = os.path.join(base_path, 'actions')
 | 
			
		||||
 | 
			
		||||
    metadata = utils.yaml_load(base_meta_file)
 | 
			
		||||
    metadata['id'] = name
 | 
			
		||||
    metadata['version'] = '1.0.0'
 | 
			
		||||
    metadata['base_path'] = os.path.abspath(base_path)
 | 
			
		||||
 | 
			
		||||
    prepare_meta(metadata)
 | 
			
		||||
 | 
			
		||||
    if os.path.exists(actions_path):
 | 
			
		||||
        for f in os.listdir(actions_path):
 | 
			
		||||
            metadata['actions'][os.path.splitext(f)[0]] = f
 | 
			
		||||
 | 
			
		||||
    tags = metadata.get('tags', [])
 | 
			
		||||
 | 
			
		||||
    resource = Resource(name, metadata, args, tags, virtual_resource)
 | 
			
		||||
    return resource
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def create_virtual_resource(vr_name, template):
 | 
			
		||||
    resources = template['resources']
 | 
			
		||||
    connections = []
 | 
			
		||||
    created_resources = []
 | 
			
		||||
 | 
			
		||||
    cwd = os.getcwd()
 | 
			
		||||
    for resource in resources:
 | 
			
		||||
        name = resource['id']
 | 
			
		||||
        base_path = os.path.join(cwd, resource['from'])
 | 
			
		||||
        args = resource['values']
 | 
			
		||||
        new_resources = create(name, base_path, args, vr_name)
 | 
			
		||||
        created_resources += new_resources
 | 
			
		||||
 | 
			
		||||
        if not is_virtual(base_path):
 | 
			
		||||
            for key, arg in args.items():
 | 
			
		||||
                if isinstance(arg, basestring) and '::' in arg:
 | 
			
		||||
                    emitter, src = arg.split('::')
 | 
			
		||||
                    connections.append((emitter, name, {src: key}))
 | 
			
		||||
 | 
			
		||||
        db = load_all()
 | 
			
		||||
        for emitter, reciver, mapping in connections:
 | 
			
		||||
            emitter = db[emitter]
 | 
			
		||||
            reciver = db[reciver]
 | 
			
		||||
            signals.connect(emitter, reciver, mapping)
 | 
			
		||||
 | 
			
		||||
    return created_resources
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def create(name, base_path, kwargs, virtual_resource=None):
 | 
			
		||||
    if isinstance(base_path, provider.BaseProvider):
 | 
			
		||||
        base_path = base_path.directory
 | 
			
		||||
 | 
			
		||||
    if not os.path.exists(base_path):
 | 
			
		||||
        raise Exception(
 | 
			
		||||
            'Base resource does not exist: {0}'.format(base_path)
 | 
			
		||||
        )
 | 
			
		||||
 | 
			
		||||
    if is_virtual(base_path):
 | 
			
		||||
        template = _compile_file(name, base_path, kwargs)
 | 
			
		||||
        yaml_template = yaml.load(StringIO(template))
 | 
			
		||||
        resources = create_virtual_resource(name, yaml_template)
 | 
			
		||||
    else:
 | 
			
		||||
        resource = create_resource(name, base_path, kwargs, virtual_resource)
 | 
			
		||||
        resources = [resource]
 | 
			
		||||
 | 
			
		||||
    return resources
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def prepare_meta(meta):
 | 
			
		||||
    actions_path = os.path.join(meta['base_path'], 'actions')
 | 
			
		||||
    meta['actions_path'] = actions_path
 | 
			
		||||
    meta['base_name'] = os.path.split(meta['base_path'])[-1]
 | 
			
		||||
 | 
			
		||||
    meta['actions'] = {}
 | 
			
		||||
    if os.path.exists(meta['actions_path']):
 | 
			
		||||
        for f in os.listdir(meta['actions_path']):
 | 
			
		||||
            meta['actions'][os.path.splitext(f)[0]] = f
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def validate_resources():
 | 
			
		||||
    db = load_all()
 | 
			
		||||
    all_errors = []
 | 
			
		||||
    for r in db.values():
 | 
			
		||||
        if not isinstance(r, Resource):
 | 
			
		||||
            continue
 | 
			
		||||
 | 
			
		||||
        errors = validation.validate_resource(r)
 | 
			
		||||
        if errors:
 | 
			
		||||
            all_errors.append((r, errors))
 | 
			
		||||
    return all_errors
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def find_inputs_without_source():
 | 
			
		||||
    """Find resources and inputs values of which are hardcoded.
 | 
			
		||||
 | 
			
		||||
    :return: [(resource_name, input_name)]
 | 
			
		||||
    """
 | 
			
		||||
    resources = load_all()
 | 
			
		||||
 | 
			
		||||
    ret = set([(r.name, input_name) for r in resources.values()
 | 
			
		||||
               for input_name in r.args])
 | 
			
		||||
 | 
			
		||||
    clients = signals.Connections.read_clients()
 | 
			
		||||
 | 
			
		||||
    for dest_dict in clients.values():
 | 
			
		||||
        for destinations in dest_dict.values():
 | 
			
		||||
            for receiver_name, receiver_input in destinations:
 | 
			
		||||
                try:
 | 
			
		||||
                    ret.remove((receiver_name, receiver_input))
 | 
			
		||||
                except KeyError:
 | 
			
		||||
                    continue
 | 
			
		||||
 | 
			
		||||
    return list(ret)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def find_missing_connections():
 | 
			
		||||
    """Find resources whose input values are duplicated
 | 
			
		||||
 | 
			
		||||
    and they are not connected between each other (i.e. the values
 | 
			
		||||
    are hardcoded, not coming from connection).
 | 
			
		||||
 | 
			
		||||
    NOTE: this we could have 2 inputs of the same value living in 2 "circles".
 | 
			
		||||
    This is not covered, we find only inputs whose value is hardcoded.
 | 
			
		||||
 | 
			
		||||
    :return: [(resource_name1, input_name1, resource_name2, input_name2)]
 | 
			
		||||
    """
 | 
			
		||||
    ret = set()
 | 
			
		||||
 | 
			
		||||
    resources = load_all()
 | 
			
		||||
 | 
			
		||||
    inputs_without_source = find_inputs_without_source()
 | 
			
		||||
 | 
			
		||||
    for resource1, input1 in inputs_without_source:
 | 
			
		||||
        r1 = resources[resource1]
 | 
			
		||||
        v1 = r1.args[input1]
 | 
			
		||||
 | 
			
		||||
        for resource2, input2 in inputs_without_source:
 | 
			
		||||
            r2 = resources[resource2]
 | 
			
		||||
            v2 = r2.args[input2]
 | 
			
		||||
 | 
			
		||||
            if v1 == v2 and resource1 != resource2 and \
 | 
			
		||||
                    (resource2, input2, resource1, input1) not in ret:
 | 
			
		||||
                ret.add((resource1, input1, resource2, input2))
 | 
			
		||||
 | 
			
		||||
    return list(ret)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def _compile_file(name, path, kwargs):
 | 
			
		||||
    with open(path) as f:
 | 
			
		||||
        content = f.read()
 | 
			
		||||
 | 
			
		||||
    inputs = get_inputs(content)
 | 
			
		||||
    template = _get_template(name, content, kwargs, inputs)
 | 
			
		||||
    return template
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def get_inputs(content):
 | 
			
		||||
    env = Environment()
 | 
			
		||||
    ast = env.parse(content)
 | 
			
		||||
    return meta.find_undeclared_variables(ast)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def _get_template(name, content, kwargs, inputs):
 | 
			
		||||
    missing = []
 | 
			
		||||
    for input in inputs:
 | 
			
		||||
        if input not in kwargs:
 | 
			
		||||
            missing.append(input)
 | 
			
		||||
    if missing:
 | 
			
		||||
        raise Exception('[{0}] Validation error. Missing data in input: {1}'.format(name, missing))
 | 
			
		||||
    template = Template(content)
 | 
			
		||||
    template = template.render(str=str, zip=zip, **kwargs)
 | 
			
		||||
    return template
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def is_virtual(path):
 | 
			
		||||
    return os.path.isfile(path)
 | 
			
		||||
 | 
			
		||||
@@ -1,276 +0,0 @@
 | 
			
		||||
# -*- coding: utf-8 -*-
 | 
			
		||||
from collections import defaultdict
 | 
			
		||||
import itertools
 | 
			
		||||
import networkx as nx
 | 
			
		||||
 | 
			
		||||
from solar.core.log import log
 | 
			
		||||
from solar.interfaces.db import get_db
 | 
			
		||||
 | 
			
		||||
db = get_db()
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class Connections(object):
 | 
			
		||||
    @staticmethod
 | 
			
		||||
    def read_clients():
 | 
			
		||||
        """
 | 
			
		||||
        Returned structure is:
 | 
			
		||||
 | 
			
		||||
        emitter_name:
 | 
			
		||||
          emitter_input_name:
 | 
			
		||||
            - - dst_name
 | 
			
		||||
              - dst_input_name
 | 
			
		||||
 | 
			
		||||
        while DB structure is:
 | 
			
		||||
 | 
			
		||||
        emitter_name_key:
 | 
			
		||||
          emitter: emitter_name
 | 
			
		||||
          sources:
 | 
			
		||||
            emitter_input_name:
 | 
			
		||||
              - - dst_name
 | 
			
		||||
                - dst_input_name
 | 
			
		||||
        """
 | 
			
		||||
 | 
			
		||||
        ret = {}
 | 
			
		||||
 | 
			
		||||
        for data in db.get_list(collection=db.COLLECTIONS.connection):
 | 
			
		||||
            ret[data['emitter']] = data['sources']
 | 
			
		||||
 | 
			
		||||
        return ret
 | 
			
		||||
 | 
			
		||||
    @staticmethod
 | 
			
		||||
    def save_clients(clients):
 | 
			
		||||
        data = []
 | 
			
		||||
 | 
			
		||||
        for emitter_name, sources in clients.items():
 | 
			
		||||
            data.append((
 | 
			
		||||
                emitter_name,
 | 
			
		||||
                {
 | 
			
		||||
                    'emitter': emitter_name,
 | 
			
		||||
                    'sources': sources,
 | 
			
		||||
                }))
 | 
			
		||||
 | 
			
		||||
        db.save_list(data, collection=db.COLLECTIONS.connection)
 | 
			
		||||
 | 
			
		||||
    @staticmethod
 | 
			
		||||
    def add(emitter, src, receiver, dst):
 | 
			
		||||
        if src not in emitter.args:
 | 
			
		||||
            return
 | 
			
		||||
 | 
			
		||||
        clients = Connections.read_clients()
 | 
			
		||||
 | 
			
		||||
        # TODO: implement general circular detection, this one is simple
 | 
			
		||||
        if [emitter.name, src] in clients.get(receiver.name, {}).get(dst, []):
 | 
			
		||||
            raise Exception('Attempted to create cycle in dependencies. Not nice.')
 | 
			
		||||
 | 
			
		||||
        clients.setdefault(emitter.name, {})
 | 
			
		||||
        clients[emitter.name].setdefault(src, [])
 | 
			
		||||
        if [receiver.name, dst] not in clients[emitter.name][src]:
 | 
			
		||||
            clients[emitter.name][src].append([receiver.name, dst])
 | 
			
		||||
 | 
			
		||||
        Connections.save_clients(clients)
 | 
			
		||||
 | 
			
		||||
    @staticmethod
 | 
			
		||||
    def remove(emitter, src, receiver, dst):
 | 
			
		||||
        clients = Connections.read_clients()
 | 
			
		||||
 | 
			
		||||
        clients[emitter.name][src] = [
 | 
			
		||||
            destination for destination in clients[emitter.name][src]
 | 
			
		||||
            if destination != [receiver.name, dst]
 | 
			
		||||
        ]
 | 
			
		||||
 | 
			
		||||
        Connections.save_clients(clients)
 | 
			
		||||
 | 
			
		||||
    @staticmethod
 | 
			
		||||
    def receivers(emitter_name, emitter_input_name):
 | 
			
		||||
        return Connections.read_clients().get(emitter_name, {}).get(
 | 
			
		||||
            emitter_input_name, []
 | 
			
		||||
        )
 | 
			
		||||
 | 
			
		||||
    @staticmethod
 | 
			
		||||
    def emitter(receiver_name, receiver_input_name):
 | 
			
		||||
        for emitter_name, dest_dict in Connections.read_clients().items():
 | 
			
		||||
            for emitter_input_name, destinations in dest_dict.items():
 | 
			
		||||
                if [receiver_name, receiver_input_name] in destinations:
 | 
			
		||||
                    return [emitter_name, emitter_input_name]
 | 
			
		||||
 | 
			
		||||
    @staticmethod
 | 
			
		||||
    def clear():
 | 
			
		||||
        db.clear_collection(collection=db.COLLECTIONS.connection)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def guess_mapping(emitter, receiver):
 | 
			
		||||
    """Guess connection mapping between emitter and receiver.
 | 
			
		||||
 | 
			
		||||
    Suppose emitter and receiver have common inputs:
 | 
			
		||||
    ip, ssh_key, ssh_user
 | 
			
		||||
 | 
			
		||||
    Then we return a connection mapping like this:
 | 
			
		||||
 | 
			
		||||
    {
 | 
			
		||||
        'ip': '<receiver>.ip',
 | 
			
		||||
        'ssh_key': '<receiver>.ssh_key',
 | 
			
		||||
        'ssh_user': '<receiver>.ssh_user'
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    :param emitter:
 | 
			
		||||
    :param receiver:
 | 
			
		||||
    :return:
 | 
			
		||||
    """
 | 
			
		||||
    guessed = {}
 | 
			
		||||
    for key in emitter.args:
 | 
			
		||||
        if key in receiver.args:
 | 
			
		||||
            guessed[key] = key
 | 
			
		||||
 | 
			
		||||
    return guessed
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def connect_single(emitter, src, receiver, dst):
 | 
			
		||||
    # Disconnect all receiver inputs
 | 
			
		||||
    # Check if receiver input is of list type first
 | 
			
		||||
    if receiver.args[dst].type_ != 'list':
 | 
			
		||||
        disconnect_receiver_by_input(receiver, dst)
 | 
			
		||||
 | 
			
		||||
    emitter.args[src].subscribe(receiver.args[dst])
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def connect(emitter, receiver, mapping=None):
 | 
			
		||||
    mapping = mapping or guess_mapping(emitter, receiver)
 | 
			
		||||
 | 
			
		||||
    if isinstance(mapping, set):
 | 
			
		||||
        for src in mapping:
 | 
			
		||||
            connect_single(emitter, src, receiver, src)
 | 
			
		||||
        return
 | 
			
		||||
 | 
			
		||||
    for src, dst in mapping.items():
 | 
			
		||||
        if isinstance(dst, list):
 | 
			
		||||
            for d in dst:
 | 
			
		||||
                connect_single(emitter, src, receiver, d)
 | 
			
		||||
            continue
 | 
			
		||||
 | 
			
		||||
        connect_single(emitter, src, receiver, dst)
 | 
			
		||||
 | 
			
		||||
    #receiver.save()
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def disconnect(emitter, receiver):
 | 
			
		||||
    clients = Connections.read_clients()
 | 
			
		||||
 | 
			
		||||
    for src, destinations in clients[emitter.name].items():
 | 
			
		||||
        for destination in destinations:
 | 
			
		||||
            receiver_input = destination[1]
 | 
			
		||||
            if receiver_input in receiver.args:
 | 
			
		||||
                if receiver.args[receiver_input].type_ != 'list':
 | 
			
		||||
                    log.debug(
 | 
			
		||||
                        'Removing input %s from %s', receiver_input, receiver.name
 | 
			
		||||
                    )
 | 
			
		||||
                emitter.args[src].unsubscribe(receiver.args[receiver_input])
 | 
			
		||||
 | 
			
		||||
        disconnect_by_src(emitter.name, src, receiver)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def disconnect_receiver_by_input(receiver, input):
 | 
			
		||||
    """Find receiver connection by input and disconnect it.
 | 
			
		||||
 | 
			
		||||
    :param receiver:
 | 
			
		||||
    :param input:
 | 
			
		||||
    :return:
 | 
			
		||||
    """
 | 
			
		||||
    clients = Connections.read_clients()
 | 
			
		||||
 | 
			
		||||
    for emitter_name, inputs in clients.items():
 | 
			
		||||
        disconnect_by_src(emitter_name, input, receiver)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def disconnect_by_src(emitter_name, src, receiver):
 | 
			
		||||
    clients = Connections.read_clients()
 | 
			
		||||
 | 
			
		||||
    if src in clients[emitter_name]:
 | 
			
		||||
        clients[emitter_name][src] = [
 | 
			
		||||
            destination for destination in clients[emitter_name][src]
 | 
			
		||||
            if destination[0] != receiver.name
 | 
			
		||||
        ]
 | 
			
		||||
 | 
			
		||||
        Connections.save_clients(clients)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def notify(source, key, value):
 | 
			
		||||
    from solar.core.resource import load
 | 
			
		||||
 | 
			
		||||
    clients = Connections.read_clients()
 | 
			
		||||
 | 
			
		||||
    if source.name not in clients:
 | 
			
		||||
        clients[source.name] = {}
 | 
			
		||||
        Connections.save_clients(clients)
 | 
			
		||||
 | 
			
		||||
    log.debug('Notify %s %s %s %s', source.name, key, value, clients[source.name])
 | 
			
		||||
    if key in clients[source.name]:
 | 
			
		||||
        for client, r_key in clients[source.name][key]:
 | 
			
		||||
            resource = load(client)
 | 
			
		||||
            log.debug('Resource found: %s', client)
 | 
			
		||||
            if resource:
 | 
			
		||||
                resource.update({r_key: value}, emitter=source)
 | 
			
		||||
            else:
 | 
			
		||||
                log.debug('Resource %s deleted?', client)
 | 
			
		||||
                pass
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def assign_connections(receiver, connections):
 | 
			
		||||
    mappings = defaultdict(list)
 | 
			
		||||
    for key, dest in connections.iteritems():
 | 
			
		||||
        resource, r_key = dest.split('.')
 | 
			
		||||
        mappings[resource].append([r_key, key])
 | 
			
		||||
    for resource, r_mappings in mappings.iteritems():
 | 
			
		||||
        connect(resource, receiver, r_mappings)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def connection_graph():
 | 
			
		||||
    resource_dependencies = {}
 | 
			
		||||
 | 
			
		||||
    clients = Connections.read_clients()
 | 
			
		||||
 | 
			
		||||
    for emitter_name, destination_values in clients.items():
 | 
			
		||||
        resource_dependencies.setdefault(emitter_name, set())
 | 
			
		||||
        for emitter_input, receivers in destination_values.items():
 | 
			
		||||
            resource_dependencies[emitter_name].update(
 | 
			
		||||
                receiver[0] for receiver in receivers
 | 
			
		||||
            )
 | 
			
		||||
 | 
			
		||||
    g = nx.DiGraph()
 | 
			
		||||
 | 
			
		||||
    # TODO: tags as graph node attributes
 | 
			
		||||
    for emitter_name, receivers in resource_dependencies.items():
 | 
			
		||||
        g.add_node(emitter_name)
 | 
			
		||||
        g.add_nodes_from(receivers)
 | 
			
		||||
        g.add_edges_from(
 | 
			
		||||
            itertools.izip(
 | 
			
		||||
                itertools.repeat(emitter_name),
 | 
			
		||||
                receivers
 | 
			
		||||
            )
 | 
			
		||||
        )
 | 
			
		||||
 | 
			
		||||
    return g
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def detailed_connection_graph(start_with=None, end_with=None):
 | 
			
		||||
    g = nx.MultiDiGraph()
 | 
			
		||||
 | 
			
		||||
    clients = Connections.read_clients()
 | 
			
		||||
 | 
			
		||||
    for emitter_name, destination_values in clients.items():
 | 
			
		||||
        for emitter_input, receivers in destination_values.items():
 | 
			
		||||
            for receiver_name, receiver_input in receivers:
 | 
			
		||||
                label = '{}:{}'.format(emitter_input, receiver_input)
 | 
			
		||||
                g.add_edge(emitter_name, receiver_name, label=label)
 | 
			
		||||
 | 
			
		||||
    ret = g
 | 
			
		||||
 | 
			
		||||
    if start_with is not None:
 | 
			
		||||
        ret = g.subgraph(
 | 
			
		||||
            nx.dfs_postorder_nodes(ret, start_with)
 | 
			
		||||
        )
 | 
			
		||||
    if end_with is not None:
 | 
			
		||||
        ret = g.subgraph(
 | 
			
		||||
            nx.dfs_postorder_nodes(ret.reverse(), end_with)
 | 
			
		||||
        )
 | 
			
		||||
 | 
			
		||||
    return ret
 | 
			
		||||
		Reference in New Issue
	
	Block a user