Merge branch 'master' into dry-run
This commit is contained in:
commit
8700bf3aa9
@ -36,57 +36,114 @@ def deploy():
|
|||||||
|
|
||||||
signals.Connections.clear()
|
signals.Connections.clear()
|
||||||
|
|
||||||
node1 = vr.create('node1', 'resources/ro_node/', {'ip': '10.0.0.3', 'ssh_key': '/vagrant/.vagrant/machines/solar-dev1/virtualbox/private_key', 'ssh_user': 'vagrant'})[0]
|
node1 = vr.create('nodes', 'templates/nodes.yml', {})[0]
|
||||||
|
|
||||||
# MARIADB
|
# MARIADB
|
||||||
mariadb_service1 = vr.create('mariadb_service1', 'resources/mariadb_service', {'image': 'mariadb', 'root_password': 'mariadb', 'port': 3306})[0]
|
mariadb_service1 = vr.create('mariadb_service1', 'resources/mariadb_service', {
|
||||||
|
'image': 'mariadb',
|
||||||
|
'port': 3306
|
||||||
|
})[0]
|
||||||
|
|
||||||
signals.connect(node1, mariadb_service1)
|
signals.connect(node1, mariadb_service1)
|
||||||
|
|
||||||
# RABBIT
|
# RABBIT
|
||||||
rabbitmq_service1 = vr.create('rabbitmq1', 'resources/rabbitmq_service', {'management_port': 15672, 'port': 5672})[0]
|
rabbitmq_service1 = vr.create('rabbitmq_service1', 'resources/rabbitmq_service/', {
|
||||||
openstack_vhost = vr.create('openstack_vhost', 'resources/rabbitmq_vhost/', {'vhost_name': 'openstack'})[0]
|
'management_port': 15672,
|
||||||
openstack_rabbitmq_user = vr.create('openstack_rabbitmq_user', 'resources/rabbitmq_user/', {'user_name': 'openstack', 'password': 'openstack_password'})[0]
|
'port': 5672,
|
||||||
|
})[0]
|
||||||
|
openstack_vhost = vr.create('openstack_vhost', 'resources/rabbitmq_vhost/', {
|
||||||
|
'vhost_name': 'openstack'
|
||||||
|
})[0]
|
||||||
|
|
||||||
|
openstack_rabbitmq_user = vr.create('openstack_rabbitmq_user', 'resources/rabbitmq_user/', {
|
||||||
|
'user_name': 'openstack',
|
||||||
|
'password': 'openstack_password'
|
||||||
|
})[0]
|
||||||
|
|
||||||
signals.connect(node1, rabbitmq_service1)
|
signals.connect(node1, rabbitmq_service1)
|
||||||
signals.connect(rabbitmq_service1, openstack_vhost)
|
signals.connect(rabbitmq_service1, openstack_vhost)
|
||||||
signals.connect(rabbitmq_service1, openstack_rabbitmq_user)
|
signals.connect(rabbitmq_service1, openstack_rabbitmq_user)
|
||||||
signals.connect(openstack_vhost, openstack_rabbitmq_user, {'vhost_name': 'vhost_name'})
|
signals.connect(openstack_vhost, openstack_rabbitmq_user, {
|
||||||
|
'vhost_name',
|
||||||
|
})
|
||||||
|
|
||||||
# KEYSTONE
|
# KEYSTONE
|
||||||
keystone_puppet = vr.create('keystone_puppet', 'resources/keystone_puppet', {})[0]
|
keystone_puppet = vr.create('keystone_puppet', 'resources/keystone_puppet', {})[0]
|
||||||
keystone_db = vr.create('keystone_db', 'resources/mariadb_keystone_db/', {'db_name': 'keystone_db', 'login_user': 'root'})[0]
|
keystone_db = vr.create('keystone_db', 'resources/mariadb_keystone_db/', {
|
||||||
keystone_db_user = vr.create('keystone_db_user', 'resources/mariadb_keystone_user/', {'new_user_name': 'keystone', 'new_user_password': 'keystone', 'login_user': 'root'})[0]
|
'db_name': 'keystone_db',
|
||||||
keystone_service_endpoint = vr.create('keystone_service_endpoint', 'resources/keystone_service_endpoint', {'endpoint_name': 'keystone', 'adminurl': 'http://{{admin_ip}}:{{admin_port}}/v2.0', 'internalurl': 'http://{{internal_ip}}:{{internal_port}}/v2.0', 'publicurl': 'http://{{public_ip}}:{{public_port}}/v2.0', 'description': 'OpenStack Identity Service', 'type': 'identity'})[0]
|
'login_user': 'root'
|
||||||
|
})[0]
|
||||||
|
keystone_db_user = vr.create('keystone_db_user', 'resources/mariadb_keystone_user/', {
|
||||||
|
'new_user_name': 'keystone',
|
||||||
|
'new_user_password': 'keystone',
|
||||||
|
})[0]
|
||||||
|
keystone_service_endpoint = vr.create('keystone_service_endpoint', 'resources/keystone_service_endpoint', {
|
||||||
|
'endpoint_name': 'keystone',
|
||||||
|
'adminurl': 'http://{{admin_ip}}:{{admin_port}}/v2.0',
|
||||||
|
'internalurl': 'http://{{internal_ip}}:{{internal_port}}/v2.0',
|
||||||
|
'publicurl': 'http://{{public_ip}}:{{public_port}}/v2.0',
|
||||||
|
'description': 'OpenStack Identity Service',
|
||||||
|
'type': 'identity'
|
||||||
|
})[0]
|
||||||
|
|
||||||
admin_tenant = vr.create('admin_tenant', 'resources/keystone_tenant', {'tenant_name': 'admin'})[0]
|
admin_tenant = vr.create('admin_tenant', 'resources/keystone_tenant', {
|
||||||
admin_user = vr.create('admin_user', 'resources/keystone_user', {'user_name': 'admin', 'user_password': 'admin'})[0]
|
'tenant_name': 'admin'
|
||||||
admin_role = vr.create('admin_role', 'resources/keystone_role', {'role_name': 'admin'})[0]
|
})[0]
|
||||||
services_tenant = vr.create('services_tenant', 'resources/keystone_tenant', {'tenant_name': 'services'})[0]
|
admin_user = vr.create('admin_user', 'resources/keystone_user', {
|
||||||
|
'user_name': 'admin',
|
||||||
|
'user_password': 'admin'
|
||||||
|
})[0]
|
||||||
|
admin_role = vr.create('admin_role', 'resources/keystone_role', {
|
||||||
|
'role_name': 'admin'
|
||||||
|
})[0]
|
||||||
|
services_tenant = vr.create('services_tenant', 'resources/keystone_tenant', {
|
||||||
|
'tenant_name': 'services'
|
||||||
|
})[0]
|
||||||
|
|
||||||
signals.connect(node1, keystone_db)
|
signals.connect(node1, keystone_db)
|
||||||
signals.connect(node1, keystone_db_user)
|
signals.connect(node1, keystone_db_user)
|
||||||
signals.connect(node1, keystone_puppet)
|
signals.connect(node1, keystone_puppet)
|
||||||
signals.connect(mariadb_service1, keystone_db, {'port': 'login_port', 'root_password': 'login_password'})
|
signals.connect(mariadb_service1, keystone_db, {
|
||||||
signals.connect(mariadb_service1, keystone_db_user, {'port': 'login_port', 'root_password': 'login_password'})
|
'port': 'login_port',
|
||||||
signals.connect(keystone_db, keystone_db_user, {'db_name': 'db_name'})
|
'root_user': 'login_user',
|
||||||
|
'root_password': 'login_password',
|
||||||
|
})
|
||||||
|
signals.connect(keystone_db, keystone_db_user, {
|
||||||
|
'db_name',
|
||||||
|
'login_port',
|
||||||
|
'login_user',
|
||||||
|
'login_password'
|
||||||
|
})
|
||||||
|
|
||||||
signals.connect(node1, keystone_service_endpoint)
|
signals.connect(node1, keystone_service_endpoint)
|
||||||
signals.connect(keystone_puppet, keystone_service_endpoint, {'admin_token': 'admin_token', 'admin_port': 'keystone_admin_port', 'ip': 'keystone_host'})
|
signals.connect(keystone_puppet, keystone_service_endpoint, {
|
||||||
signals.connect(keystone_puppet, keystone_service_endpoint, {'admin_port': 'admin_port', 'ip': 'admin_ip'})
|
'admin_token': 'admin_token',
|
||||||
signals.connect(keystone_puppet, keystone_service_endpoint, {'port': 'internal_port', 'ip': 'internal_ip'})
|
'admin_port': 'keystone_admin_port',
|
||||||
signals.connect(keystone_puppet, keystone_service_endpoint, {'port': 'public_port', 'ip': 'public_ip'})
|
'ip': ['keystone_host', 'admin_ip', 'internal_ip', 'public_ip'],
|
||||||
|
'port': ['admin_port', 'internal_port', 'public_port'],
|
||||||
|
})
|
||||||
|
|
||||||
signals.connect(keystone_puppet, admin_tenant)
|
signals.connect(keystone_puppet, admin_tenant)
|
||||||
signals.connect(keystone_puppet, admin_tenant, {'admin_port': 'keystone_port', 'ip': 'keystone_host'})
|
signals.connect(keystone_puppet, admin_tenant, {
|
||||||
|
'admin_port': 'keystone_port',
|
||||||
|
'ip': 'keystone_host'
|
||||||
|
})
|
||||||
signals.connect(admin_tenant, admin_user)
|
signals.connect(admin_tenant, admin_user)
|
||||||
signals.connect(admin_user, admin_role)
|
signals.connect(admin_user, admin_role)
|
||||||
|
|
||||||
signals.connect(keystone_puppet, services_tenant)
|
signals.connect(keystone_puppet, services_tenant)
|
||||||
signals.connect(keystone_puppet, services_tenant, {'admin_port': 'keystone_port', 'ip': 'keystone_host'})
|
signals.connect(keystone_puppet, services_tenant, {
|
||||||
|
'admin_port': 'keystone_port',
|
||||||
|
'ip': 'keystone_host'
|
||||||
|
})
|
||||||
|
|
||||||
signals.connect(keystone_db, keystone_puppet, {'db_name': 'db_name'})
|
signals.connect(keystone_db, keystone_puppet, {
|
||||||
signals.connect(keystone_db_user, keystone_puppet, {'new_user_name': 'db_user', 'new_user_password': 'db_password'})
|
'db_name',
|
||||||
|
})
|
||||||
|
signals.connect(keystone_db_user, keystone_puppet, {
|
||||||
|
'new_user_name': 'db_user',
|
||||||
|
'new_user_password': 'db_password'
|
||||||
|
})
|
||||||
|
|
||||||
# OPENRC
|
# OPENRC
|
||||||
openrc = vr.create('openrc_file', 'resources/openrc_file', {})[0]
|
openrc = vr.create('openrc_file', 'resources/openrc_file', {})[0]
|
||||||
@ -97,23 +154,54 @@ def deploy():
|
|||||||
|
|
||||||
# NEUTRON
|
# NEUTRON
|
||||||
# TODO: vhost cannot be specified in neutron Puppet manifests so this user has to be admin anyways
|
# TODO: vhost cannot be specified in neutron Puppet manifests so this user has to be admin anyways
|
||||||
neutron_puppet = vr.create('neutron_puppet', 'resources/neutron_puppet', {'rabbitmq_user': 'guest', 'rabbitmq_password': 'guest'})[0]
|
neutron_puppet = vr.create('neutron_puppet', 'resources/neutron_puppet', {
|
||||||
|
'rabbitmq_user': 'guest',
|
||||||
|
'rabbitmq_password': 'guest'
|
||||||
|
})[0]
|
||||||
|
|
||||||
neutron_keystone_user = vr.create('neutron_keystone_user', 'resources/keystone_user', {'user_name': 'neutron', 'user_password': 'neutron'})[0]
|
neutron_keystone_user = vr.create('neutron_keystone_user', 'resources/keystone_user', {
|
||||||
neutron_keystone_role = vr.create('neutron_keystone_role', 'resources/keystone_role', {'role_name': 'neutron'})[0]
|
'user_name': 'neutron',
|
||||||
neutron_keystone_service_endpoint = vr.create('neutron_keystone_service_endpoint', 'resources/keystone_service_endpoint', {'endpoint_name': 'neutron', 'adminurl': 'http://{{admin_ip}}:{{admin_port}}', 'internalurl': 'http://{{internal_ip}}:{{internal_port}}', 'publicurl': 'http://{{public_ip}}:{{public_port}}', 'description': 'OpenStack Network Service', 'type': 'network'})[0]
|
'user_password': 'neutron'
|
||||||
|
})[0]
|
||||||
|
neutron_keystone_role = vr.create('neutron_keystone_role', 'resources/keystone_role', {
|
||||||
|
'role_name': 'neutron'
|
||||||
|
})[0]
|
||||||
|
neutron_keystone_service_endpoint = vr.create('neutron_keystone_service_endpoint', 'resources/keystone_service_endpoint', {
|
||||||
|
'endpoint_name': 'neutron',
|
||||||
|
'adminurl': 'http://{{admin_ip}}:{{admin_port}}',
|
||||||
|
'internalurl': 'http://{{internal_ip}}:{{internal_port}}',
|
||||||
|
'publicurl': 'http://{{public_ip}}:{{public_port}}',
|
||||||
|
'description': 'OpenStack Network Service',
|
||||||
|
'type': 'network'
|
||||||
|
})[0]
|
||||||
|
|
||||||
signals.connect(node1, neutron_puppet)
|
signals.connect(node1, neutron_puppet)
|
||||||
signals.connect(rabbitmq_service1, neutron_puppet, {'ip': 'rabbitmq_host', 'port': 'rabbitmq_port'})
|
signals.connect(rabbitmq_service1, neutron_puppet, {
|
||||||
signals.connect(admin_user, neutron_puppet, {'user_name': 'keystone_user', 'user_password': 'keystone_password', 'tenant_name': 'keystone_tenant'})
|
'ip': 'rabbitmq_host',
|
||||||
signals.connect(keystone_puppet, neutron_puppet, {'ip': 'keystone_host', 'port': 'keystone_port'})
|
'port': 'rabbitmq_port'
|
||||||
|
})
|
||||||
|
signals.connect(admin_user, neutron_puppet, {
|
||||||
|
'user_name': 'keystone_user',
|
||||||
|
'user_password': 'keystone_password',
|
||||||
|
'tenant_name': 'keystone_tenant'
|
||||||
|
})
|
||||||
|
signals.connect(keystone_puppet, neutron_puppet, {
|
||||||
|
'ip': 'keystone_host',
|
||||||
|
'port': 'keystone_port'
|
||||||
|
})
|
||||||
signals.connect(services_tenant, neutron_keystone_user)
|
signals.connect(services_tenant, neutron_keystone_user)
|
||||||
signals.connect(neutron_keystone_user, neutron_keystone_role)
|
signals.connect(neutron_keystone_user, neutron_keystone_role)
|
||||||
signals.connect(keystone_puppet, neutron_keystone_service_endpoint, {'ip': 'ip', 'ssh_key': 'ssh_key', 'ssh_user': 'ssh_user'})
|
signals.connect(keystone_puppet, neutron_keystone_service_endpoint, {
|
||||||
signals.connect(neutron_puppet, neutron_keystone_service_endpoint, {'ip': 'admin_ip', 'port': 'admin_port'})
|
'ip': ['ip', 'keystone_host'],
|
||||||
signals.connect(neutron_puppet, neutron_keystone_service_endpoint, {'ip': 'internal_ip', 'port': 'internal_port'})
|
'ssh_key': 'ssh_key',
|
||||||
signals.connect(neutron_puppet, neutron_keystone_service_endpoint, {'ip': 'public_ip', 'port': 'public_port'})
|
'ssh_user': 'ssh_user',
|
||||||
signals.connect(keystone_puppet, neutron_keystone_service_endpoint, {'ip': 'keystone_host', 'admin_port': 'keystone_admin_port', 'admin_token': 'admin_token'})
|
'admin_port': 'keystone_admin_port',
|
||||||
|
'admin_token': 'admin_token',
|
||||||
|
})
|
||||||
|
signals.connect(neutron_puppet, neutron_keystone_service_endpoint, {
|
||||||
|
'ip': ['admin_ip', 'internal_ip', 'public_ip'],
|
||||||
|
'port': ['admin_port', 'internal_port', 'public_port'],
|
||||||
|
})
|
||||||
|
|
||||||
# # CINDER
|
# # CINDER
|
||||||
# cinder_puppet = vr.create('cinder_puppet', 'resources/cinder_puppet', {
|
# cinder_puppet = vr.create('cinder_puppet', 'resources/cinder_puppet', {
|
||||||
|
1
main.yml
1
main.yml
@ -11,6 +11,7 @@
|
|||||||
- shell: pip install httpie
|
- shell: pip install httpie
|
||||||
#- apt: name=python-virtualenv state=present
|
#- apt: name=python-virtualenv state=present
|
||||||
#- apt: name=virtualenvwrapper state=present
|
#- apt: name=virtualenvwrapper state=present
|
||||||
|
- apt: name=python-mock state=present
|
||||||
- apt: name=ipython state=present
|
- apt: name=ipython state=present
|
||||||
- apt: name=python-pudb state=present
|
- apt: name=python-pudb state=present
|
||||||
#- apt: name=python-pip state=present
|
#- apt: name=python-pip state=present
|
||||||
|
@ -38,7 +38,7 @@ input:
|
|||||||
value:
|
value:
|
||||||
internalurl:
|
internalurl:
|
||||||
schema: str!
|
schema: str!
|
||||||
value: http://{{ip}}:{{port}}/v2.0
|
value: http://{{internal_ip}}:{{internal_port}}/v2.0
|
||||||
admin_ip:
|
admin_ip:
|
||||||
schema: str!
|
schema: str!
|
||||||
value:
|
value:
|
||||||
@ -47,7 +47,7 @@ input:
|
|||||||
value:
|
value:
|
||||||
adminurl:
|
adminurl:
|
||||||
schema: str!
|
schema: str!
|
||||||
value: http://{{ip}}:{{admin_port}}/v2.0
|
value: http://{{admin_ip}}:{{admin_port}}/v2.0
|
||||||
|
|
||||||
ip:
|
ip:
|
||||||
schema: str!
|
schema: str!
|
||||||
|
@ -18,7 +18,7 @@ def test(resource):
|
|||||||
resp_json = resp.json()
|
resp_json = resp.json()
|
||||||
assert 'services' in resp_json
|
assert 'services' in resp_json
|
||||||
|
|
||||||
service = [s for s in resp_json['services'] if s['name'] == resource.name][0]
|
service = [s for s in resp_json['services'] if s['name'] == resource.args['endpoint_name'].value][0]
|
||||||
service_id = service['id']
|
service_id = service['id']
|
||||||
|
|
||||||
assert service['description'] == resource.args['description'].value
|
assert service['description'] == resource.args['description'].value
|
||||||
|
@ -8,6 +8,7 @@ input:
|
|||||||
db_name:
|
db_name:
|
||||||
schema: str!
|
schema: str!
|
||||||
value:
|
value:
|
||||||
|
|
||||||
login_user:
|
login_user:
|
||||||
schema: str!
|
schema: str!
|
||||||
value:
|
value:
|
||||||
@ -17,6 +18,7 @@ input:
|
|||||||
login_port:
|
login_port:
|
||||||
schema: int!
|
schema: int!
|
||||||
value:
|
value:
|
||||||
|
|
||||||
ip:
|
ip:
|
||||||
schema: str!
|
schema: str!
|
||||||
value:
|
value:
|
||||||
|
@ -4,6 +4,8 @@
|
|||||||
- name: mariadb db
|
- name: mariadb db
|
||||||
mysql_db:
|
mysql_db:
|
||||||
name: {{ db_name }}
|
name: {{ db_name }}
|
||||||
|
#collation: utf8_encode_ci
|
||||||
|
encoding: utf8
|
||||||
state: present
|
state: present
|
||||||
login_user: root
|
login_user: root
|
||||||
login_password: {{ login_password }}
|
login_password: {{ login_password }}
|
||||||
|
@ -5,6 +5,9 @@ input:
|
|||||||
image:
|
image:
|
||||||
schema: str!
|
schema: str!
|
||||||
value: mariadb
|
value: mariadb
|
||||||
|
root_user:
|
||||||
|
schema: str!
|
||||||
|
value: root
|
||||||
root_password:
|
root_password:
|
||||||
schema: str!
|
schema: str!
|
||||||
value: mariadb
|
value: mariadb
|
||||||
|
@ -11,9 +11,11 @@ input:
|
|||||||
user_name:
|
user_name:
|
||||||
schema: str!
|
schema: str!
|
||||||
value:
|
value:
|
||||||
|
|
||||||
db_name:
|
db_name:
|
||||||
schema: str!
|
schema: str!
|
||||||
value:
|
value:
|
||||||
|
|
||||||
login_password:
|
login_password:
|
||||||
schema: str!
|
schema: str!
|
||||||
value:
|
value:
|
||||||
@ -23,6 +25,7 @@ input:
|
|||||||
login_user:
|
login_user:
|
||||||
schema: str!
|
schema: str!
|
||||||
value:
|
value:
|
||||||
|
|
||||||
ip:
|
ip:
|
||||||
schema: str!
|
schema: str!
|
||||||
value:
|
value:
|
||||||
|
@ -6,7 +6,7 @@ networkx==1.9.1
|
|||||||
PyYAML==3.11
|
PyYAML==3.11
|
||||||
jsonschema==2.4.0
|
jsonschema==2.4.0
|
||||||
requests==2.7.0
|
requests==2.7.0
|
||||||
mock
|
#mock
|
||||||
dictdiffer==0.4.0
|
dictdiffer==0.4.0
|
||||||
enum34==1.0.4
|
enum34==1.0.4
|
||||||
redis==2.10.3
|
redis==2.10.3
|
||||||
|
@ -7,7 +7,7 @@ from fabric.contrib import project as fabric_project
|
|||||||
import os
|
import os
|
||||||
|
|
||||||
from solar.core.log import log
|
from solar.core.log import log
|
||||||
from solar.core.handlers.base import BaseHandler
|
from solar.core.handlers.base import TempFileHandler
|
||||||
from solar.core.provider import GitProvider
|
from solar.core.provider import GitProvider
|
||||||
|
|
||||||
|
|
||||||
@ -151,7 +151,7 @@ class LibrarianPuppet(ResourceSSHMixin):
|
|||||||
# - hiera-redis is installed with the 2.0 fix (https://github.com/GGenie/hiera-redis)
|
# - hiera-redis is installed with the 2.0 fix (https://github.com/GGenie/hiera-redis)
|
||||||
# - redis is installed and cluster set up with master (on slaves set up 'slaveof 10.0.0.2 6379')
|
# - redis is installed and cluster set up with master (on slaves set up 'slaveof 10.0.0.2 6379')
|
||||||
# - redis keys are separated by colon (same as in hiera-redis backend)
|
# - redis keys are separated by colon (same as in hiera-redis backend)
|
||||||
class Puppet(ResourceSSHMixin, BaseHandler):
|
class Puppet(ResourceSSHMixin, TempFileHandler):
|
||||||
def action(self, resource, action_name):
|
def action(self, resource, action_name):
|
||||||
log.debug('Executing Puppet manifest %s %s', action_name, resource)
|
log.debug('Executing Puppet manifest %s %s', action_name, resource)
|
||||||
|
|
||||||
|
@ -124,16 +124,30 @@ def guess_mapping(emitter, receiver):
|
|||||||
return guessed
|
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):
|
def connect(emitter, receiver, mapping=None):
|
||||||
mapping = mapping or guess_mapping(emitter, receiver)
|
mapping = mapping or guess_mapping(emitter, receiver)
|
||||||
|
|
||||||
for src, dst in mapping.items():
|
if isinstance(mapping, set):
|
||||||
# Disconnect all receiver inputs
|
for src in mapping:
|
||||||
# Check if receiver input is of list type first
|
connect_single(emitter, src, receiver, src)
|
||||||
if receiver.args[dst].type_ != 'list':
|
return
|
||||||
disconnect_receiver_by_input(receiver, dst)
|
|
||||||
|
|
||||||
emitter.args[src].subscribe(receiver.args[dst])
|
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()
|
#receiver.save()
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user