Add link local services provisioning support
Change-Id: I9a9721f8469e355c7187ab9c54338e70a62ab2b1
diff --git a/README.rst b/README.rst
index 7ad6f17..1e97333 100644
--- a/README.rst
+++ b/README.rst
@@ -849,6 +849,45 @@
nal02:
ip_address: 172.16.0.32
+Enforcing Link Local Services
+
+.. code-block:: yaml
+
+ opencontrail:
+ client:
+ ...
+ linklocal_service:
+ # example with dns name address (only one permited)
+ meta1:
+ lls_ip: 10.0.0.23
+ lls_port: 80
+ ipf_addresses: "meta.example.com"
+ ipf_port: 80
+ # example with multiple ip addresses
+ meta2:
+ lls_ip: 10.0.0.23
+ lls_port: 80
+ ipf_addresses:
+ - 10.10.10.10
+ - 10.20.20.20
+ - 10.30.30.30
+ ipf_port: 80
+ # example with one ip address
+ meta3:
+ lls_ip: 10.0.0.23
+ lls_port: 80
+ ipf_addresses:
+ - 10.10.10.10
+ ipf_port: 80
+ # example with name override
+ lls_meta4:
+ name: meta4
+ lls_ip: 10.0.0.23
+ lls_port: 80
+ ipf_addresses:
+ - 10.10.10.10
+ ipf_port: 80
+
Usage
=====
diff --git a/_modules/contrail.py b/_modules/contrail.py
index 2cd1747..11d7ca1 100644
--- a/_modules/contrail.py
+++ b/_modules/contrail.py
@@ -17,6 +17,8 @@
try:
from vnc_api import vnc_api
+ from vnc_api.vnc_api import LinklocalServiceEntryType, \
+ LinklocalServicesTypes, GlobalVrouterConfig
from vnc_api.gen.resource_client import VirtualRouter, AnalyticsNode, \
ConfigNode, DatabaseNode, BgpRouter
from vnc_api.gen.resource_xsd import AddressFamilies, BgpSessionAttributes, \
@@ -504,3 +506,160 @@
database_node_obj = databaseNode(name, gsc_obj)
vnc_client.database_node_delete(
fq_name=database_node_obj.get_fq_name())
+
+
+
+
+def _get_vrouter_config(vnc_client):
+ try:
+ config = vnc_client.global_vrouter_config_read(
+ fq_name=['default-global-system-config', 'default-global-vrouter-config'])
+ except Exception:
+ config = None
+
+ return config
+
+
+
+def linklocal_service_list(**kwargs):
+ '''
+ Return a list of all Contrail link local services
+
+ CLI Example:
+
+ .. code-block:: bash
+
+ salt '*' contrail.linklocal_service_list
+ '''
+ ret = {}
+ vnc_client = _auth(**kwargs)
+
+ current_config = _get_vrouter_config(vnc_client)
+ if current_config is None:
+ return ret
+
+ service_list_res = current_config.get_linklocal_services()
+ if service_list_res is None:
+ service_list_obj = {'linklocal_service_entry': []}
+ else:
+ service_list_obj = service_list_res.__dict__
+ for _, value in service_list_obj.iteritems():
+ for entry in value:
+ service = entry.__dict__
+ if 'linklocal_service_name' in service:
+ ret[service['linklocal_service_name']] = service
+ return ret
+
+
+def linklocal_service_get(name, **kwargs):
+ '''
+ Return a specific Contrail link local service
+
+ CLI Example:
+
+ .. code-block:: bash
+
+ salt '*' contrail.linklocal_service_get llservice
+ '''
+ ret = {}
+ services = linklocal_service_list(**kwargs)
+ if name in services:
+ ret[name] = services.get(name)
+ if len(ret) == 0:
+ return {'Error': 'Error in retrieving link local service "{0}"'.format(name)}
+ return ret
+
+
+def linklocal_service_create(name, lls_ip, lls_port, ipf_dns_or_ip, ipf_port, **kwargs):
+ '''
+ Create specific Contrail link local service
+
+ CLI Example:
+
+ .. code-block:: bash
+
+ salt '*' contrail.linklocal_service_create \
+ llservice 10.10.10.103 22 '["20.20.20.20", "30.30.30.30"]' 22
+ salt '*' contrail.linklocal_service_create \
+ llservice 10.10.10.103 22 link-local.service.dns-name 22
+ '''
+ ret = {}
+ vnc_client = _auth(**kwargs)
+
+ current_config = _get_vrouter_config(vnc_client)
+
+ service_entry = LinklocalServiceEntryType(
+ linklocal_service_name=name,
+ linklocal_service_ip=lls_ip,
+ linklocal_service_port=lls_port,
+ ip_fabric_service_port=ipf_port)
+ if isinstance(ipf_dns_or_ip, basestring):
+ service_entry.ip_fabric_DNS_service_name = ipf_dns_or_ip
+ elif isinstance(ipf_dns_or_ip, list):
+ service_entry.ip_fabric_service_ip = ipf_dns_or_ip
+ service_entry.ip_fabric_DNS_service_name = ''
+
+ if current_config is None:
+ new_services = LinklocalServicesTypes([service_entry])
+ new_config = GlobalVrouterConfig(linklocal_services=new_services)
+ vnc_client.global_vrouter_config_create(new_config)
+ else:
+ _current_service_list = current_config.get_linklocal_services()
+ if _current_service_list is None:
+ service_list = {'linklocal_service_entry': []}
+ else:
+ service_list = _current_service_list.__dict__
+ new_services = [service_entry]
+ for key, value in service_list.iteritems():
+ if key != 'linklocal_service_entry':
+ continue
+ for _entry in value:
+ entry = _entry.__dict__
+ if 'linklocal_service_name' in entry:
+ if entry['linklocal_service_name'] == name:
+ return {'Error': 'Link local service "{0}" already exists'.format(name)}
+ new_services.append(_entry)
+ service_list[key] = new_services
+ new_config = GlobalVrouterConfig(linklocal_services=service_list)
+ vnc_client.global_vrouter_config_update(new_config)
+ ret = linklocal_service_list(**kwargs)
+ return ret[name]
+
+
+def linklocal_service_delete(name, **kwargs):
+ '''
+ Delete specific link local service entry
+
+ CLI Example:
+
+ .. code-block:: bash
+
+ salt '*' contrail.linklocal_service_delete llservice
+ '''
+ vnc_client = _auth(**kwargs)
+
+ current_config = _get_vrouter_config(vnc_client)
+
+ found = False
+ if current_config is not None:
+ _current_service_list = current_config.get_linklocal_services()
+ if _current_service_list is None:
+ service_list = {'linklocal_service_entry': []}
+ else:
+ service_list = _current_service_list.__dict__
+ new_services = []
+ for key, value in service_list.iteritems():
+ if key != 'linklocal_service_entry':
+ continue
+ for _entry in value:
+ entry = _entry.__dict__
+ if 'linklocal_service_name' in entry:
+ if entry['linklocal_service_name'] == name:
+ found = True
+ else:
+ new_services.append(_entry)
+ service_list[key] = new_services
+ new_config = GlobalVrouterConfig(linklocal_services=service_list)
+ vnc_client.global_vrouter_config_update(new_config)
+ if not found:
+ return {'Error': 'Link local service "{0}" not found'.format(name)}
diff --git a/_states/contrail.py b/_states/contrail.py
index f117667..17f579d 100644
--- a/_states/contrail.py
+++ b/_states/contrail.py
@@ -41,6 +41,53 @@
name: cmp01
+Enforce the link local service entry existence
+----------------------------------------------
+
+.. code-block:: yaml
+
+ # Example with dns name, only one is permited
+ lls_meta1:
+ contrail.linklocal_service_present:
+ - name: meta1
+ - lls_ip: 10.0.0.23
+ - lls_port: 80
+ - ipf_addresses: "meta.example.com"
+ - ipf_port: 80
+
+ # Example with multiple ip addresses
+ lls_meta2:
+ contrail.linklocal_service_present:
+ - name: meta2
+ - lls_ip: 10.0.0.23
+ - lls_port: 80
+ - ipf_addresses:
+ - 10.10.10.10
+ - 10.20.20.20
+ - 10.30.30.30
+ - ipf_port: 80
+
+ # Example with one ip addresses
+ lls_meta3:
+ contrail.linklocal_service_present:
+ - name: meta3
+ - lls_ip: 10.0.0.23
+ - lls_port: 80
+ - ipf_addresses:
+ - 10.10.10.10
+ - ipf_port: 80
+
+
+Enforce the link local service entry absence
+--------------------------------------------
+
+.. code-block:: yaml
+
+ lls_meta1_delete:
+ contrail.linklocal_service_absent:
+ - name: cmp01
+
+
Enforce the analytics node existence
------------------------------------
@@ -122,6 +169,46 @@
return ret
+def linklocal_service_present(name, lls_ip, lls_port, ipf_addresses, ipf_port, **kwargs):
+ '''
+ Ensures that the Contrail link local service entry exists.
+
+ :param name: Link local service name
+ :param lls_ip: Link local ip address
+ :param lls_port: Link local service port
+ :param ipf_addresses: IP fabric dns name or list of IP fabric ip addresses
+ :param ipf_port: IP fabric port
+ '''
+ ret = {'name': name,
+ 'changes': {},
+ 'result': True,
+ 'comment': 'Link local service "{0}" already exists'.format(name)}
+ lls = __salt__['contrail.linklocal_service_get'](name, **kwargs)
+ if 'Error' in lls:
+ __salt__['contrail.linklocal_service_create'](name, lls_ip, lls_port, ipf_addresses, ipf_port, **kwargs)
+ ret['comment'] = 'Link local service "{0}" has been created'.format(name)
+ ret['changes']['LinkLocalService'] = 'Created'
+ return ret
+
+
+def linklocal_service_absent(name, **kwargs):
+ '''
+ Ensure that the Contrail link local service entry doesn't exist
+
+ :param name: The name of the link local service entry
+ '''
+ ret = {'name': name,
+ 'changes': {},
+ 'result': True,
+ 'comment': ' "{0}" is already absent'.format(name)}
+ lls = __salt__['contrail.linklocal_service_get'](name, **kwargs)
+ if 'Error' not in lls:
+ __salt__['contrail.linklocal_service_delete'](name, **kwargs)
+ ret['comment'] = 'Link local service "{0}" has been deleted'.format(name)
+ ret['changes']['LinkLocalService'] = 'Deleted'
+
+ return ret
+
def analytics_node_present(name, ip_address, **kwargs):
'''
Ensures that the Contrail analytics node exists.
diff --git a/opencontrail/client.sls b/opencontrail/client.sls
index 61b999c..35fe914 100644
--- a/opencontrail/client.sls
+++ b/opencontrail/client.sls
@@ -99,4 +99,23 @@
{%- endfor %}
+{%- for linklocal_service_name, linklocal_service in client.get('linklocal_service', {}).items() %}
+
+opencontrail_client_linklocal_service_{{ linklocal_service_name }}:
+ contrail.linklocal_service_present:
+ - name: {{ linklocal_service.get('name', linklocal_service_name) }}
+ - lls_ip: {{ linklocal_service.get('lls_ip') }}
+ - lls_port: {{ linklocal_service.get('lls_port') }}
+ - ipf_addresses: {{ linklocal_service.get('ipf_addresses') }}
+ - ipf_port: {{ linklocal_service.get('ipf_port') }}
+ - user: {{ client.identity.user }}
+ - password: {{ client.identity.password }}
+ - project: {{ client.identity.tenant }}
+ - auth_host_ip: {{ client.identity.host }}
+ - api_server_ip: {{ client.api.host }}
+ - api_server_port: {{ client.api.port }}
+ - api_base_url: '/'
+
+{%- endfor %}
+
{%- endif %}