Merge "Extended client role"
diff --git a/_modules/contrail.py b/_modules/contrail.py
index 11d7ca1..3719653 100644
--- a/_modules/contrail.py
+++ b/_modules/contrail.py
@@ -14,6 +14,8 @@
 # limitations under the License.
 
 from netaddr import IPNetwork
+from vnc_api.vnc_api import PhysicalRouter, PhysicalInterface, LogicalInterface
+from vnc_api.vnc_api import EncapsulationPrioritiesType
 
 try:
     from vnc_api import vnc_api
@@ -23,6 +25,7 @@
         ConfigNode, DatabaseNode, BgpRouter
     from vnc_api.gen.resource_xsd import AddressFamilies, BgpSessionAttributes, \
         BgpSession, BgpPeeringAttributes, BgpRouterParams
+
     HAS_CONTRAIL = True
 except ImportError:
     HAS_CONTRAIL = False
@@ -53,13 +56,13 @@
     use_ssl = False
     auth_host = kwargs.get('auth_host_ip')
     vnc_lib = vnc_api.VncApi(user, password, tenant_name,
-        api_host, api_port, api_base_url, wait_for_connect=True,
-        api_server_use_ssl=use_ssl, auth_host=auth_host)
+                             api_host, api_port, api_base_url, wait_for_connect=True,
+                             api_server_use_ssl=use_ssl, auth_host=auth_host)
 
     return vnc_lib
 
 
-def _get_config(vnc_client, global_system_config = 'default-global-system-config'):
+def _get_config(vnc_client, global_system_config='default-global-system-config'):
     try:
         gsc_obj = vnc_client.global_system_config_read(id=global_system_config)
     except vnc_api.NoIdError:
@@ -71,7 +74,6 @@
 
 
 def _get_rt_inst_obj(vnc_client):
-
     # TODO pick fqname hardcode from common
     rt_inst_obj = vnc_client.routing_instance_read(
         fq_name=['default-domain', 'default-project',
@@ -84,7 +86,6 @@
     return str(IPNetwork(ip_w_pfx).ip)
 
 
-
 def virtual_router_list(**kwargs):
     '''
     Return a list of all Contrail virtual routers
@@ -101,7 +102,9 @@
     for vrouter_obj in vrouter_objs:
         ret[vrouter_obj.name] = {
             'ip_address': vrouter_obj.virtual_router_ip_address,
-            'dpdk_enabled': vrouter_obj.virtual_router_dpdk_enabled
+            'dpdk_enabled': vrouter_obj.virtual_router_dpdk_enabled,
+            'uuid': vrouter_obj.uuid
+
         }
     return ret
 
@@ -125,7 +128,7 @@
     return ret
 
 
-def virtual_router_create(name, ip_address, dpdk_enabled=False, **kwargs):
+def virtual_router_create(name, ip_address, router_type=None, dpdk_enabled=False, **kwargs):
     '''
     Create specific Contrail virtual router
 
@@ -134,21 +137,48 @@
     .. code-block:: bash
 
         salt '*' contrail.virtual_router_create cmp02 10.10.10.102
+        router_types:
+        - tor-agent
+        - tor-service-node
+        - embedded
     '''
     ret = {}
     vnc_client = _auth(**kwargs)
     gsc_obj = _get_config(vnc_client)
     vrouter_objs = virtual_router_list(**kwargs)
     if name in vrouter_objs:
-        return {'Error': 'Virtual router %s already exists' % name}
+        vrouter = virtual_router_get(name)
+        vrouter_obj = vnc_client._object_read('virtual-router', id=vrouter[name]['uuid'])
+        changed = False
+        if vrouter_obj.get_virtual_router_ip_address() != ip_address:
+            ret['ip_address'] = {'from': vrouter_obj.get_virtual_router_ip_address(), "to": ip_address}
+            vrouter_obj.set_virtual_router_ip_address(ip_address)
+            changed = True
+        if vrouter_obj.get_virtual_router_type() != router_type:
+            ret['router_type'] = {"from": vrouter_obj.get_virtual_router_type(), "to": router_type}
+            vrouter_obj.set_virtual_router_type(router_type)
+            changed = True
+        if vrouter_obj.get_virtual_router_dpdk_enabled() != dpdk_enabled:
+            ret['dpdk_enabled'] = {"from": vrouter_obj.get_virtual_router_dpdk_enabled(), "to": dpdk_enabled}
+            vrouter_obj.set_virtual_router_dpdk_enabled(dpdk_enabled)
+            changed = True
+        if changed:
+            if __opts__['test']:
+                return "Virtual router " + name + " will be updated"
+            vnc_client.virtual_router_update(vrouter_obj)
+            return ret
+        return {'OK': 'Virtual router %s already exists and is updated' % name}
     else:
         vrouter_obj = VirtualRouter(
             name, gsc_obj,
-            virtual_router_ip_address=ip_address)
+            virtual_router_ip_address=ip_address,
+            virtual_router_type=router_type)
         vrouter_obj.set_virtual_router_dpdk_enabled(dpdk_enabled)
+        if __opts__['test']:
+            return "Virtual router " + name + " will be created"
         vnc_client.virtual_router_create(vrouter_obj)
     ret = virtual_router_list(**kwargs)
-    return ret[name]
+    return "Create"
 
 
 def virtual_router_delete(name, **kwargs):
@@ -164,8 +194,462 @@
     vnc_client = _auth(**kwargs)
     gsc_obj = _get_config(vnc_client)
     vrouter_obj = VirtualRouter(name, gsc_obj)
+    if __opts__['test']:
+        return "Virtual router " + name + " will be deleted"
     vnc_client.virtual_router_delete(
         fq_name=vrouter_obj.get_fq_name())
+    return "Deleted"
+
+
+def physical_router_list(**kwargs):
+    '''
+    Return a list of all Contrail physical routers
+
+    CLI Example:
+
+    .. code-block:: bash
+
+        salt '*' contrail.physical_router_list
+    '''
+    ret = {}
+    vnc_client = _auth(**kwargs)
+    prouter_objs = vnc_client._objects_list('physical-router', detail=True)
+    for prouter_obj in prouter_objs:
+        ret[prouter_obj.name] = {
+            'uuid': prouter_obj._uuid,
+            'management_ip': prouter_obj._physical_router_management_ip,
+            'product_name': prouter_obj._physical_router_product_name,
+        }
+
+    return ret
+
+
+def physical_router_get(name, **kwargs):
+    '''
+    Return a specific Contrail physical router
+
+    CLI Example:
+
+    .. code-block:: bash
+
+        salt '*' contrail.physical_router_get router_name
+    '''
+    ret = {}
+    vnc_client = _auth(**kwargs)
+    prouter_objs = vnc_client._objects_list('physical-router', detail=True)
+    for prouter_obj in prouter_objs:
+        if name == prouter_obj.name:
+            ret[name] = prouter_obj.__dict__
+    if len(ret) == 0:
+        return {'Error': 'Error in retrieving physical router.'}
+    return ret
+
+
+def physical_router_create(name, parent_type=None,
+                           management_ip=None,
+                           dataplane_ip=None,  # VTEP address in web GUI
+                           vendor_name=None,
+                           product_name=None,
+                           vnc_managed=None,
+                           junos_service_ports=None,
+                           agents=None, **kwargs):
+    '''
+    Create specific Contrail physical router
+
+    CLI Example:
+
+    .. code-block:: bash
+
+        salt '*' contrail.physical_router_create OVSDB_router management_ip=10.167.4.202 dataplane_ip=172.16.20.15 vendor_name=MyVendor product_name=MyProduct agents="['tor01','tns01']"
+    '''
+    ret = {}
+    vnc_client = _auth(**kwargs)
+    gsc_obj = _get_config(vnc_client)
+    prouter_objs = physical_router_list(**kwargs)
+    if name in prouter_objs:
+        prouter = physical_router_get(name)
+        prouter_obj = vnc_client._object_read('physical-router', id=prouter[name]['_uuid'])
+        if prouter_obj.physical_router_management_ip != management_ip:
+            ret['management_ip'] = {'from': prouter_obj.physical_router_management_ip, "to": management_ip}
+            prouter_obj.set_physical_router_management_ip(management_ip)
+        if prouter_obj.physical_router_dataplane_ip != dataplane_ip:
+            ret['dataplane_ip'] = {'from': prouter_obj.physical_router_dataplane_ip, "to": dataplane_ip}
+            prouter_obj.set_physical_router_dataplane_ip(dataplane_ip)
+        if prouter_obj.get_physical_router_vendor_name() != vendor_name:
+            ret['vendor_name'] = {'from': prouter_obj.get_physical_router_vendor_name(), "to": vendor_name}
+            prouter_obj.set_physical_router_vendor_name(vendor_name)
+        if prouter_obj.get_physical_router_product_name() != product_name:
+            ret['product_name'] = {'from': prouter_obj.get_physical_router_product_name(), "to": product_name}
+            prouter_obj.set_physical_router_product_name(product_name)
+        if prouter_obj.get_physical_router_vnc_managed() != vnc_managed:
+            ret['vnc_managed'] = {'from': prouter_obj.get_physical_router_vnc_managed(), "to": vnc_managed}
+            prouter_obj.set_physical_router_vnc_managed(vnc_managed)
+        if prouter_obj.get_physical_router_junos_service_ports() != junos_service_ports:
+            ret['junos_service_ports'] = {'from': prouter_obj.get_physical_router_junos_service_ports(),
+                                          "to": junos_service_ports}
+            prouter_obj.set_physical_router_junos_service_ports(junos_service_ports)
+
+        if __opts__['test']:
+            if len(ret) != 0:
+                return "Physical router " + name + " will be updated"
+            return {"OK": "Physical router exists and is updated"}
+
+        vrouter_objs = vnc_client._objects_list('virtual-router', detail=True)  # all vrouter objects
+        c_agents = []  # referenced vrouters
+        for c_agent in prouter_obj.get_virtual_router_refs():
+            c_agents.append(c_agent['uuid'])
+        agent_objs = []  # required state of references
+        for vrouter_obj in vrouter_objs:
+            if vrouter_obj._display_name in agents and vrouter_obj._uuid not in c_agents:
+                prouter_obj.add_virtual_router(vrouter_obj)
+                ret['vrouter ' + vrouter_obj._display_name] = "Reference added"
+            if vrouter_obj._display_name not in agents and vrouter_obj._uuid in c_agents:
+                prouter_obj.del_virtual_router(vrouter_obj)
+                ret['vrouter ' + vrouter_obj._display_name] = "Reference removed"
+        vnc_client.physical_router_update(prouter_obj)
+
+        if len(ret) == 0:
+            return {"OK": "Physical router exists and is updated"}
+        return ret
+    else:
+        if __opts__['test']:
+            return "Physical router " + name + " will be created"
+        prouter_obj = PhysicalRouter(
+            name=name,
+            parent_obj=None,
+            physical_router_management_ip=management_ip,
+            physical_router_dataplane_ip=dataplane_ip,
+            physical_router_vendor_name=vendor_name,
+            physical_router_product_name=product_name,
+            physical_router_vnc_managed=vnc_managed,
+            physical_router_junos_service_ports=junos_service_ports,
+        )
+        for agent in agents:
+            vrouter = virtual_router_get(agent)
+            vrouter_obj = vnc_client._object_read('virtual-router', id=vrouter[agent]['uuid'])
+            prouter_obj.add_virtual_router(vrouter_obj)
+        vnc_client.physical_router_create(prouter_obj)
+    return "Created"
+
+
+def physical_router_delete(name, **kwargs):
+    '''
+    Delete specific Contrail physical router
+
+    CLI Example:
+
+    .. code-block:: bash
+
+        salt '*' contrail.physical_router_delete router_name
+    '''
+    vnc_client = _auth(**kwargs)
+    gsc_obj = _get_config(vnc_client)
+    prouter_obj = PhysicalRouter(name, gsc_obj)
+    if __opts__['test']:
+        return "Physical router " + name + " will be deleted"
+    vnc_client.physical_router_delete(
+        fq_name=prouter_obj.get_fq_name())
+    return "Deleted"
+
+
+def physical_interface_list(**kwargs):
+    '''
+    Return a list of all Contrail physical interface
+
+    CLI Example:
+
+    .. code-block:: bash
+
+        salt '*' contrail.physical_interface_list
+    '''
+    ret = {}
+    vnc_client = _auth(**kwargs)
+    pinterface_objs = vnc_client._objects_list('physical-interface', detail=True)
+    for pinterface_obj in pinterface_objs:
+        ret[pinterface_obj.name] = {
+            'uuid': pinterface_obj._uuid,
+            'fq_name': pinterface_obj.fq_name,
+            'parent_type': pinterface_obj.parent_type,
+        }
+
+    return ret
+
+
+def physical_interface_get(name, physical_router, **kwargs):
+    '''
+    Return a specific Contrail physical interface
+
+    CLI Example:
+
+    .. code-block:: bash
+
+        salt '*' contrail.physical_interface_get interface_name physical_router_name
+    '''
+    ret = {}
+    vnc_client = _auth(**kwargs)
+    pinterf_objs = vnc_client._objects_list('physical-interface', detail=True)
+    for pinterf_obj in pinterf_objs:
+        if name == pinterf_obj.name and physical_router in pinterf_obj.fq_name:
+            ret[name] = pinterf_obj.__dict__
+    if len(ret) == 0:
+        return {'Error': 'Error in retrieving physical interface.'}
+    return ret
+
+
+def physical_interface_create(name, physical_router, **kwargs):
+    '''
+    Create specific Contrail physical interface
+
+    CLI Example:
+
+    .. code-block:: bash
+
+        salt '*' contrail.physical_interface_create ge-0/0/10 physical_router_name
+    '''
+    ret = {}
+    vnc_client = _auth(**kwargs)
+    gsc_obj = _get_config(vnc_client)
+    pinterf_obj = physical_interface_get(name, physical_router, **kwargs)
+    if 'Error' not in pinterf_obj:
+        return {'OK': 'Physical interface ' + name + ' on ' + physical_router + ' already exists'}
+    else:
+        if __opts__['test']:
+            return "Physical interface " + name + " will be created"
+        prouter = physical_router_get(physical_router)
+        prouter_obj = vnc_client._object_read('physical-router', id=prouter[physical_router]['_uuid'])
+        pinterf_obj = PhysicalInterface(name, prouter_obj)
+        vnc_client.physical_interface_create(pinterf_obj)
+    return "Created"
+
+
+def physical_interface_delete(name, physical_router, **kwargs):
+    '''
+    Delete specific Contrail physical interface
+
+    CLI Example:
+    .. code-block:: bash
+
+        salt '*' contrail.physical_interface_delete ge-0/0/0 phr01
+    '''
+    vnc_client = _auth(**kwargs)
+    gsc_obj = _get_config(vnc_client)
+    piface = physical_interface_get(name, physical_router)
+    if __opts__['test']:
+        return "Physical interface " + name + " will be deleted"
+    vnc_client.physical_interface_delete(id=piface[name]['_uuid'])
+    return "Deleted"
+
+
+def logical_interface_list(**kwargs):
+    '''
+    Return a list of all Contrail logical interfaces
+
+    CLI Example:
+
+    .. code-block:: bash
+
+        salt '*' contrail.logical_interface_list
+    '''
+    ret = []
+    vnc_client = _auth(**kwargs)
+    liface_objs = vnc_client._objects_list('logical-interface', detail=True)
+    for liface_obj in liface_objs:
+        ret.append({
+            'name': liface_obj.name,
+            'uuid': liface_obj._uuid,
+            'fq_name': liface_obj.fq_name,
+            'parent_type': liface_obj.parent_type,
+        })
+    return ret
+
+
+def logical_interface_get(name, parent_names, parent_type=None, **kwargs):
+    '''
+    Return a specific Contrail logical interface
+
+    CLI Example:
+
+    .. code-block:: bash
+
+        salt '*' contrail.logical_interface_get ge-0/0/0.10 ['phr01']
+        or
+        salt '*' contrail.logical_interface_get ge-0/0/0.10 ['ge-0/0/0','phr01']
+        or
+        salt '*' contrail.logical_interface_get ge-0/0/0.10 ['phr01'] parent_type=physcal-interface
+    '''
+    ret = {}
+    vnc_client = _auth(**kwargs)
+    liface_objs = vnc_client._objects_list('logical-interface', detail=True)
+    count = 0
+    for liface_obj in liface_objs:
+        if name == liface_obj.name and set(parent_names).issubset(liface_obj.fq_name):
+            if parent_type and parent_type == liface_obj.parent_type:
+                count += 1
+                ret[liface_obj.name] = liface_obj.__dict__
+            if not parent_type:
+                count += 1
+                ret[liface_obj.name] = liface_obj.__dict__
+    if len(ret) == 0:
+        return {'Error': 'Error in retrieving logical interface.'}
+    if count > 1:
+        return {
+            'Error': 'Error Was found more then one logical interface. Please put more parent_name or put parent_type to chose one of them.'}
+    return ret
+
+
+def logical_interface_create(name, parent_names, parent_type='physical-interface', vlan_tag=None, interface_type="l2",
+                             **kwargs):
+    '''
+    Create specific Contrail logical interface
+
+    CLI Example:
+
+    .. code-block:: bash
+
+        salt '*' contrail.logical_interface_create ge-0/0/10.11 parent_names="['ge-0/0/0','phr1']" parent_type=physical-interface vlan_tag=1025 interface_type=L2
+    '''
+    ret = {}
+    vnc_client = _auth(**kwargs)
+    gsc_obj = _get_config(vnc_client)
+    liface_obj = logical_interface_get(name, parent_names, parent_type, **kwargs)
+    if 'Error' not in liface_obj:
+        return {'OK': 'Logical interface ' + name + ' already exists'}
+    else:
+        if __opts__['test']:
+            return "Logical interface " + name + " will be created"
+        parent_obj = None
+        for router in parent_names:
+            parent_router = physical_router_get(router)
+            if 'Error' not in parent_router:
+                parent_obj = vnc_client._object_read('physical-router', id=parent_router[router]['_uuid'])
+                break
+        if not parent_obj:
+            return {'Error': 'Physical router have to be defined'}
+        if parent_type == 'physical-interface':
+            for interface in parent_names:
+                parent_interface = physical_interface_get(interface, parent_obj.name)
+                if 'Error' not in parent_interface:
+                    parent_obj = vnc_client._object_read('physical-interface', id=parent_interface[interface]['_uuid'])
+                    break
+        if interface_type.lower() == "l3":
+            return {'Error': "Virtual Network have to be defined for L3 interface type"}
+
+        liface_obj = LogicalInterface(name, parent_obj, vlan_tag, interface_type.lower())
+        vnc_client.logical_interface_create(liface_obj)
+    return "Created"
+
+
+def logical_interface_delete(name, parent_names, parent_type=None, **kwargs):
+    '''
+    Delete specific Contrail logical interface
+
+    CLI Example:
+
+    .. code-block:: bash
+
+        salt '*' contrail.logical_interface_delete ge-0/0/0.12 ['ge-0/0/0','phr01']
+        or
+        salt '*' contrail.logical_interface_delete ge-0/0/0.12 ['phr01'] parent_type=physical-router
+
+    '''
+    vnc_client = _auth(**kwargs)
+    gsc_obj = _get_config(vnc_client)
+    liface = logical_interface_get(name, parent_names, parent_type)
+    if 'Error' not in liface:
+        if __opts__['test']:
+            return "Logical interface " + name + " will be deleted"
+        vnc_client.logical_interface_delete(id=liface[name]['_uuid'])
+        return "Deleted"
+    else:
+        return liface
+
+
+def global_vrouter_config_list(**kwargs):
+    '''
+    Return a list of all Contrail global vrouter configs
+
+    CLI Example:
+
+    .. code-block:: bash"
+
+        salt '*' global_vrouter_config_list
+    '''
+    ret = {}
+    vnc_client = _auth(**kwargs)
+    vrouter_conf_objs = vnc_client._objects_list('global-vrouter-config', detail=True)
+    for vrouter_conf_obj in vrouter_conf_objs:
+        ret[vrouter_conf_obj._display_name] = vrouter_conf_obj.__dict__
+    return ret
+
+
+def global_vrouter_config_get(name, **kwargs):
+    '''
+    Return a specific Contrail global vrouter config
+
+    CLI Example:
+
+    .. code-block:: bash
+
+        salt '*' contrail.global_vrouter_get global-vrouter-config
+    '''
+    ret = {}
+    vrouter_conf_objs = global_vrouter_config_list(**kwargs)
+    if name in vrouter_conf_objs:
+        ret[name] = vrouter_conf_objs.get(name)
+    if len(ret) == 0:
+        return {'Error': 'Error in retrieving  global vrouter config.'}
+    return ret
+
+
+def global_vrouter_config_create(name, parent_type, encap_priority, vxlan_vn_id_mode, *fq_names, **kwargs):
+    '''
+    Create specific Contrail global vrouter config
+
+    CLI Example:
+
+    .. code-block:: bash
+
+        salt '*' contrail.global_vrouter_config_create name=global-vrouter-config parent_type=global-system-config encap_priority="MPLSoUDP,MPLSoGRE" vxlan_vn_id_mode="automatic" fq_names="['default-global-system-config', 'default-global-vrouter-config']"
+    '''
+    ret = {}
+    vnc_client = _auth(**kwargs)
+    gsc_obj = _get_config(vnc_client)
+    vrouter_conf_objs = global_vrouter_config_list(**kwargs)
+    if name in vrouter_conf_objs:
+        return {'OK': 'Global vrouter config %s already exists' % name}
+    else:
+        vrouter_conf_obj = GlobalVrouterConfig(
+            name=name,
+            parent_obj=None,
+            encapsulation_priorities=EncapsulationPrioritiesType(encapsulation=encap_priority.split(",")),
+            fq_name=fq_names,
+            vxlan_network_identifier_mode=vxlan_vn_id_mode,
+            parent_type=parent_type,
+        )
+        if __opts__['test']:
+            return "Global vRouter config " + name + " will be created"
+        vnc_client.global_vrouter_config_create(vrouter_conf_obj)
+    return "Created"
+
+
+def global_vrouter_config_delete(name, **kwargs):
+    '''
+    Delete specific Contrail global vrouter config
+
+    CLI Example:
+
+    .. code-block:: bash
+
+        salt '*' contrail.global_vrouter_config_delete global-vrouter-config
+    '''
+    vnc_client = _auth(**kwargs)
+    gsc_obj = _get_config(vnc_client)
+    vrouter_conf_obj = GlobalVrouterConfig(name, gsc_obj)
+    if __opts__['test']:
+        return "Global vRouter config " + name + " will be deleted"
+    vnc_client.global_vrouter_config_delete(
+        fq_name=vrouter_conf_obj.get_fq_name())
+    return "Deleted"
 
 
 def analytics_node_list(**kwargs):
@@ -220,14 +704,15 @@
     gsc_obj = _get_config(vnc_client)
     analytics_node_objs = analytics_node_list(**kwargs)
     if name in analytics_node_objs:
-        return {'Error': 'Analytics node %s already exists' % name}
+        return {'OK': 'Analytics node %s already exists' % name}
     else:
         analytics_node_obj = AnalyticsNode(
             name, gsc_obj,
             analytics_node_ip_address=ip_address)
+        if __opts__['test']:
+            return "AnalyticsNode " + name + " will be created"
         vnc_client.analytics_node_create(analytics_node_obj)
-    ret = analytics_node_list(**kwargs)
-    return ret[name]
+    return "Created"
 
 
 def analytics_node_delete(name, **kwargs):
@@ -243,8 +728,11 @@
     vnc_client = _auth(**kwargs)
     gsc_obj = _get_config(vnc_client)
     analytics_node_obj = AnalyticsNode(name, gsc_obj)
+    if __opts__['test']:
+        return "AnalyticsNode " + name + " will be deleted"
     vnc_client.analytics_node_delete(
         fq_name=analytics_node_obj.get_fq_name())
+    return "Deleted"
 
 
 def config_node_list(**kwargs):
@@ -299,14 +787,15 @@
     gsc_obj = _get_config(vnc_client)
     config_node_objs = config_node_list(**kwargs)
     if name in config_node_objs:
-        return {'Error': 'Config node %s already exists' % name}
+        return {'OK': 'Config node %s already exists' % name}
     else:
         config_node_obj = ConfigNode(
             name, gsc_obj,
             config_node_ip_address=ip_address)
+        if __opts__['test']:
+            return "ConfigNode " + name + " will be created"
         vnc_client.config_node_create(config_node_obj)
-    ret = config_node_list(**kwargs)
-    return ret[name]
+    return "Created"
 
 
 def config_node_delete(name, **kwargs):
@@ -322,8 +811,11 @@
     vnc_client = _auth(**kwargs)
     gsc_obj = _get_config(vnc_client)
     config_node_obj = ConfigNode(name, gsc_obj)
+    if __opts__['test']:
+        return "ConfigNode " + name + " will be deleted"
     vnc_client.config_node_delete(
         fq_name=config_node_obj.get_fq_name())
+    return "Deleted"
 
 
 def bgp_router_list(**kwargs):
@@ -377,39 +869,45 @@
     ret = {}
     vnc_client = _auth(**kwargs)
 
+    address_families = ['route-target', 'inet-vpn', 'e-vpn', 'erm-vpn',
+                        'inet6-vpn']
+    if type != 'control-node':
+        address_families.remove('erm-vpn')
+
+    bgp_addr_fams = AddressFamilies(address_families)
+    bgp_sess_attrs = [
+        BgpSessionAttributes(address_families=bgp_addr_fams)]
+    bgp_sessions = [BgpSession(attributes=bgp_sess_attrs)]
+    bgp_peering_attrs = BgpPeeringAttributes(session=bgp_sessions)
+    rt_inst_obj = _get_rt_inst_obj(vnc_client)
+
+    if type == 'control-node':
+        vendor = 'contrail'
+    elif type == 'router':
+        vendor = 'mx'
+    else:
+        vendor = 'unknown'
+
+    router_params = BgpRouterParams(router_type=type,
+                                    vendor=vendor, autonomous_system=int(asn),
+                                    identifier=_get_ip(ip_address),
+                                    address=_get_ip(ip_address),
+                                    port=179, address_families=bgp_addr_fams)
+
     bgp_router_objs = bgp_router_list(**kwargs)
     if name in bgp_router_objs:
-        return {'Error': 'control node %s already exists' % name}
+        bgp_router_obj = vnc_client._object_read('bgp-router', id=bgp_router_objs[name]['_uuid'])
+        bgp_router_obj.set_bgp_router_parameters(router_params)
+        if __opts__['test']:
+            return "BGP router " + name + " will be updated"
+        vnc_client.bgp_router_update(bgp_router_obj)
     else:
-        address_families = ['route-target', 'inet-vpn', 'e-vpn', 'erm-vpn',
-                            'inet6-vpn']
-        if type != 'control-node':
-            address_families.remove('erm-vpn')
-
-        bgp_addr_fams = AddressFamilies(address_families)
-        bgp_sess_attrs = [
-            BgpSessionAttributes(address_families=bgp_addr_fams)]
-        bgp_sessions = [BgpSession(attributes=bgp_sess_attrs)]
-        bgp_peering_attrs = BgpPeeringAttributes(session=bgp_sessions)
-        rt_inst_obj = _get_rt_inst_obj(vnc_client)
-
-        if type == 'control-node':
-            vendor = 'contrail'
-        elif type == 'router':
-            vendor = 'mx'
-        else:
-            vendor = 'unknown'
-
-        router_params = BgpRouterParams(router_type=type,
-            vendor=vendor, autonomous_system=int(asn),
-            identifier=_get_ip(ip_address),
-            address=_get_ip(ip_address),
-            port=179, address_families=bgp_addr_fams)
-        bgp_router_obj = BgpRouter(name, rt_inst_obj,
-            bgp_router_parameters=router_params)
+        bgp_router_obj = BgpRouter(name, rt_inst_obj, bgp_router_parameters=router_params)
+        if __opts__['test']:
+            return "BGP router " + name + " will be created"
         vnc_client.bgp_router_create(bgp_router_obj)
-    ret = bgp_router_list(**kwargs)
-    return ret[name]
+        return "Created"
+    return {'OK': 'Config node %s already exists' % name}
 
 
 def bgp_router_delete(name, **kwargs):
@@ -423,11 +921,16 @@
         salt '*' contrail.bgp_router_delete mx01
     '''
     vnc_client = _auth(**kwargs)
-    gsc_obj = _get_control(vnc_client)
+    gsc_obj = _get_config(vnc_client)
     bgp_router_obj = BgpRouter(name, gsc_obj)
+
+    if __opts__['test']:
+        return "BGP router " + name + " will be deleted"
     vnc_client.bgp_router_delete(
         fq_name=bgp_router_obj.get_fq_name())
 
+    return "Deleted"
+
 
 def database_node_list(**kwargs):
     '''
@@ -481,14 +984,15 @@
     gsc_obj = _get_config(vnc_client)
     database_node_objs = database_node_list(**kwargs)
     if name in database_node_objs:
-        return {'Error': 'Database node %s already exists' % name}
+        return {'OK': 'Database node %s already exists' % name}
     else:
         database_node_obj = DatabaseNode(
             name, gsc_obj,
             database_node_ip_address=ip_address)
+        if __opts__['test']:
+            return "DatabaseNode " + name + " will be created"
         vnc_client.database_node_create(database_node_obj)
-    ret = database_node_list(**kwargs)
-    return ret[name]
+    return "Created"
 
 
 def database_node_delete(name, **kwargs):
@@ -502,14 +1006,14 @@
         salt '*' contrail.database_node_delete cmp01
     '''
     vnc_client = _auth(**kwargs)
-    gsc_obj = _get_database(vnc_client)
-    database_node_obj = databaseNode(name, gsc_obj)
+    gsc_obj = _get_config(vnc_client)
+    database_node_obj = DatabaseNode(name, gsc_obj)
+    if __opts__['test']:
+        return "DatabaseNode " + name + " will be deleted"
     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(
@@ -520,7 +1024,6 @@
     return config
 
 
-
 def linklocal_service_list(**kwargs):
     '''
     Return a list of all Contrail link local services
@@ -602,7 +1105,11 @@
     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)
+        if __opts__['test']:
+            ret['GlobalVrouterConfig'] = "Global vRouter Config will be created"
+        else:
+            ret = "Created"
+            vnc_client.global_vrouter_config_create(new_config)
     else:
         _current_service_list = current_config.get_linklocal_services()
         if _current_service_list is None:
@@ -617,13 +1124,18 @@
                 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)}
+                        return {'OK': 'Link local service "{0}" already exists'.format(name)}
                     new_services.append(_entry)
+            if __opts__['test']:
+                ret['Test'] = "LinkLocalSevices will be created"
             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]
+        if __opts__['test']:
+            ret['GlobalVrouterConfig'] = "Global vRouter Config will be updated"
+        else:
+            vnc_client.global_vrouter_config_update(new_config)
+            ret = "Created"
+    return ret
 
 
 def linklocal_service_delete(name, **kwargs):
@@ -660,6 +1172,46 @@
                         new_services.append(_entry)
             service_list[key] = new_services
         new_config = GlobalVrouterConfig(linklocal_services=service_list)
+        if __opts__['test']:
+            return "Link local service " + name + " will be deleted"
         vnc_client.global_vrouter_config_update(new_config)
+        return "Deleted"
     if not found:
         return {'Error': 'Link local service "{0}" not found'.format(name)}
+
+
+def virtual_machine_interface_list(**kwargs):
+    '''
+    Return a list of all Contrail virtual machine interfaces
+
+    CLI Example:
+
+    .. code-block:: bash
+
+        salt '*' contrail.virtual_machine_interfaces
+    '''
+    ret = {}
+    vnc_client = _auth(**kwargs)
+    vm_ifaces = vnc_client._objects_list('virtual-machine-interface', detail=True)
+    for vm_iface in vm_ifaces:
+        ret[vm_iface.name] = vm_iface.__dict__
+    return ret
+
+
+def virtual_network_list(**kwargs):
+    '''
+    Return a list of all Contrail virtual network
+
+    CLI Example:
+
+    .. code-block:: bash
+
+        salt '*' contrail.virtual_network
+    '''
+
+    ret = {}
+    vnc_client = _auth(**kwargs)
+    virtual_networks = vnc_client._objects_list('virtual-network', detail=True)
+    for virtual_network in virtual_networks:
+        ret[virtual_network.name] = virtual_network.__dict__
+    return ret
diff --git a/_states/contrail.py b/_states/contrail.py
index 17f579d..e64948e 100644
--- a/_states/contrail.py
+++ b/_states/contrail.py
@@ -26,9 +26,10 @@
 
     virtual_router:
       contrail.virtual_router_present:
-        name: cmp01
+        name: tor01
         ip_address: 10.0.0.23
         dpdk_enabled: False
+        router_type: tor-agent
 
 
 Enforce the virtual router absence
@@ -36,9 +37,116 @@
 
 .. code-block:: yaml
 
-    virtual_router_cmp01:
+    virtual_router_tor01:
       contrail.virtual_router_absent:
-        name: cmp01
+        name: tor01
+
+
+Enforce the physical router existence
+------------------------------------
+
+.. code-block:: yaml
+
+    physical_router_phr01:
+      contrail.physical_router_present:
+      name: phr01
+        parent_type: global-system-config
+        management_ip: 10.167.4.206
+        dataplane_ip: 172.17.56.9
+        vendor_name: MyVendor
+        product_name: MyProduct
+        agents:
+          - tor01
+          - tns01
+
+
+Enforce the physical router absence
+----------------------------------
+
+.. code-block:: yaml
+
+    physical_router_delete_phr01:
+      contrail.physical_router_absent:
+        name: phr01
+
+
+Enforce the physical interface present
+----------------------------------
+
+.. code-block:: yaml
+
+    create physical interface ge-0/1/10 for phr01:
+      contrail.physical_interface_present:
+        - name: ge-0/1/10
+        - physical_router: prh01
+
+
+Enforce the physical interface absence
+----------------------------------
+
+.. code-block:: yaml
+
+    physical_interface_delete ge-0/1/10:
+      contrail.physical_interface_absent:
+        name: ge-0/1/10
+
+
+Enforce the logical interface present
+----------------------------------
+
+.. code-block:: yaml
+
+    create logical interface 11/15:
+      contrail.logical_interface_present:
+        - name: ge-0/1/11.15
+        - parent_names:
+          - ge-0/1/11
+          - phr01
+        - parent_type: physical-interface
+        - vlan_tag: 15
+        - interface_type: L3
+
+
+Enforce the logical interface absence
+----------------------------------
+
+.. code-block:: yaml
+
+    logical interface delete ge-0/1/10.0 phr02:
+      contrail.logical_interface_absent:
+        - name: ge-0/1/10.0
+        - parent_names:
+          - ge-0/1/10
+          - phr02
+        - parent_type: physical-interface
+
+
+Enforce the global vrouter existence
+------------------------------------
+
+.. code-block:: yaml
+
+    #Example
+    opencontrail_client_virtual_router_global_conf_create:
+      contrail.global_vrouter_config_present:
+      - name: "global-vrouter-config"
+      - parent_type: "global-system-config"
+      - encap_priority : "MPLSoUDP,MPLSoGRE"
+      - vxlan_vn_id_mode : "automatic"
+      - fq_names:
+        - default-global-system-config
+        - default-global-vrouter-config
+
+
+Enforce the global vrouter absence
+----------------------------------
+
+.. code-block:: yaml
+
+    #Example
+    opencontrail_client_virtual_router_global_conf_delete:
+      contrail.global_vrouter_config_absent:
+      - name: "global-vrouter-config"
 
 
 Enforce the link local service entry existence
@@ -122,6 +230,7 @@
 
 '''
 
+
 def __virtual__():
     '''
     Load Contrail module
@@ -129,24 +238,25 @@
     return 'contrail'
 
 
-def virtual_router_present(name, ip_address, dpdk_enabled=False, **kwargs):
+def virtual_router_present(name, ip_address, router_type=None, dpdk_enabled=False, **kwargs):
     '''
     Ensures that the Contrail virtual router exists.
 
     :param name:        Virtual router name
     :param ip_address:  Virtual router IP address
+    :param router_type: Any of ['tor-agent', 'tor-service-node', 'embedded']
     '''
     ret = {'name': name,
            'changes': {},
            'result': True,
            'comment': 'Virtual router "{0}" already exists'.format(name)}
-    virtual_router = __salt__['contrail.virtual_router_get'](name, **kwargs)
-    if 'Error' not in virtual_router:
+    result = __salt__['contrail.virtual_router_create'](name, ip_address, router_type, dpdk_enabled, **kwargs)
+    if 'OK' in result:
+        ret['comment'] = result
         pass
     else:
-        __salt__['contrail.virtual_router_create'](name, ip_address, dpdk_enabled, **kwargs)
         ret['comment'] = 'Virtual router {0} has been created'.format(name)
-        ret['changes']['VirtualRouter'] = 'Created'
+        ret['changes']['VirtualRouter'] = result
     return ret
 
 
@@ -162,10 +272,199 @@
            'comment': 'Virtual router "{0}" is already absent'.format(name)}
     virtual_router = __salt__['contrail.virtual_router_get'](name, **kwargs)
     if 'Error' not in virtual_router:
-        __salt__['contrail.virtual_router_delete'](name, **kwargs)
+        result = __salt__['contrail.virtual_router_delete'](name, **kwargs)
         ret['comment'] = 'Virtual router {0} has been deleted'.format(name)
-        ret['changes']['VirtualRouter'] = 'Deleted'
+        ret['changes']['VirtualRouter'] = result
+    return ret
 
+
+def physical_router_present(name, parent_type=None,
+                            management_ip=None,
+                            dataplane_ip=None,  # VTEP address in web GUI
+                            vendor_name=None,
+                            product_name=None,
+                            vnc_managed=None,
+                            junos_service_ports=None,
+                            agents=None, **kwargs):
+    '''
+    Ensures that the Contrail virtual router exists.
+
+    :param name:        	Physical router name
+    :param parent_type:		Parent resource type: Any of ['global-system-config']
+    :param management_ip:	Management ip for this physical router. It is used by the device manager to perform netconf and by SNMP collector if enabled.
+    :param dataplane_ip: 	VTEP address in web GUI. This is ip address in the ip-fabric(underlay) network that can be used in data plane by physical router. Usually it is the VTEP address in VxLAN for the TOR switch.
+    :param vendor_name:		Vendor name of the physical router (e.g juniper). Used by the device manager to select driver.
+    :param product_name:	Model name of the physical router (e.g juniper). Used by the device manager to select driver.
+    :param vnc_managed:		This physical router is enabled to be configured by device manager.
+    :param user_credentials:	Username and password for netconf to the physical router by device manager.
+    :param junos_service_ports:	Juniper JUNOS specific service interfaces name to perform services like NAT.
+    :param agents: 		List of virtual-router references
+    '''
+    ret = {'name': name,
+           'changes': {},
+           'result': True,
+           'comment': 'Physical router "{0}" already exists'.format(name)}
+    result = __salt__['contrail.physical_router_create'](name, parent_type, management_ip, dataplane_ip, vendor_name,
+                                                         product_name, vnc_managed, junos_service_ports, agents,
+                                                         **kwargs)
+    if 'OK' in result:
+        ret['comment'] = result
+        pass
+    else:
+        ret['comment'] = 'Physical router {0} has been created'.format(name)
+        ret['changes']['PhysicalRouter'] = result
+    return ret
+
+
+def physical_router_absent(name, **kwargs):
+    '''
+    Ensure that the Contrail physical router doesn't exist
+
+    :param name: The name of the physical router that should not exist
+    '''
+    ret = {'name': name,
+           'changes': {},
+           'result': True,
+           'comment': 'Physical router "{0}" is already absent'.format(name)}
+    physical_router = __salt__['contrail.physical_router_get'](name, **kwargs)
+    if 'Error' not in physical_router:
+        result = __salt__['contrail.physical_router_delete'](name, **kwargs)
+        ret['comment'] = 'Physical router {0} has been deleted'.format(name)
+        ret['changes']['PhysicalRouter'] = result
+    return ret
+
+
+def physical_interface_present(name, physical_router, **kwargs):
+    '''
+    Ensures that the Contrail physical interface exists.
+
+    :param name:                Physical interface name
+    :param physical_router:     Name of existing physical router
+    '''
+    ret = {'name': name,
+           'changes': {},
+           'result': True,
+           'comment': 'Physical interface "{0}" already exists'.format(name)}
+
+    result = __salt__['contrail.physical_interface_create'](name, physical_router, **kwargs)
+    if 'OK' in result:
+        ret['comment'] = result
+        pass
+    else:
+        ret['comment'] = 'Physical interface {0} has been created'.format(name)
+        ret['changes']['PhysicalInterface'] = result
+    return ret
+
+
+def physical_interface_absent(name, physical_router, **kwargs):
+    '''
+    Ensure that the Contrail physical interface doesn't exist
+
+    :param name: 		The name of the physical interface that should not exist
+    :param physical_router:     Physical router name
+    '''
+    ret = {'name': name,
+           'changes': {},
+           'result': True,
+           'comment': 'Physical interface "{0}" is already absent'.format(name)}
+    physical_interface = __salt__['contrail.physical_interface_get'](name, physical_router, **kwargs)
+    if 'Error' not in physical_interface:
+        result = __salt__['contrail.physical_interface_delete'](name, physical_router, **kwargs)
+        ret['comment'] = 'Physical interface {0} has been deleted'.format(name)
+        ret['changes']['PhysicalInterface'] = result
+    return ret
+
+
+def logical_interface_present(name, parent_names, parent_type, vlan_tag=None, interface_type="L2", **kwargs):
+    '''
+    Ensures that the Contrail logical interface exists.
+
+    :param name:                Logical interface name
+    :param parent_names:  	List of parents
+    :param parent_type		Parent resource type. Any of ['physical-router', 'physical-interface']
+    :param vlan_tag:		VLAN tag (.1Q) classifier for this logical interface.
+    :param interface_type	Logical interface type can be L2 or L3.
+    '''
+    ret = {'name': name,
+           'changes': {},
+           'result': True,
+           'comment': 'Logical interface "{0}" already exists'.format(name)}
+    logical_interface = __salt__['contrail.logical_interface_get'](name, parent_names, parent_type, **kwargs)
+    if 'Error' not in logical_interface:
+        pass
+    else:
+        result = __salt__['contrail.logical_interface_create'](name, parent_names, parent_type, vlan_tag,
+                                                               interface_type, **kwargs)
+        if 'Error' in result:
+            return False
+
+        ret['comment'] = 'Logical interface {0} has been created'.format(name)
+        ret['changes']['LogicalInterface'] = result
+    return ret
+
+
+def logical_interface_absent(name, parent_names, parent_type=None, **kwargs):
+    '''
+    Ensure that the Contrail logical interface doesn't exist
+
+    :param name: 		The name of the logical interface that should not exist
+    :param parent_names: 	List of parent names. Example ['phr01','ge-0/1/0']
+    :param parent_type: 	Parent resource type. Any of ['physical-router', 'physical-interface']
+    '''
+    ret = {'name': name,
+           'changes': {},
+           'result': True,
+           'comment': 'logical interface "{0}" is already absent'.format(name)}
+    logical_interface = __salt__['contrail.logical_interface_get'](name, parent_names, parent_type, **kwargs)
+    if 'Error' not in logical_interface:
+        result = __salt__['contrail.logical_interface_delete'](name, parent_names, parent_type, **kwargs)
+        ret['comment'] = 'Logical interface {0} has been deleted'.format(name)
+        ret['changes']['LogicalInterface'] = result
+    return ret
+
+
+def global_vrouter_config_present(name, parent_type, encap_priority="MPLSoUDP,MPLSoGRE", vxlan_vn_id_mode="automatic",
+                                  *fq_names, **kwargs):
+    '''
+    Ensures that the Contrail global vrouter config exists.
+
+    :param name:         	Global vrouter config name
+    :param parent_type:  	Parent resource type
+    :param encap_priority: 	Ordered list of encapsulations that vrouter will use in priority order
+    :param vxlan_vn_id_mode:	Method of allocation of VxLAN VNI(s).
+    :param fq_names:		Fully Qualified Name of resource devided <string>array
+    '''
+    ret = {'name': name,
+           'changes': {},
+           'result': True,
+           'comment': 'Global vrouter config "{0}" already exists'.format(name)}
+
+    result = __salt__['contrail.global_vrouter_config_create'](name, parent_type, encap_priority, vxlan_vn_id_mode,
+                                                               *fq_names, **kwargs)
+    if 'OK' in result:
+        ret['comment'] = result
+        pass
+    else:
+        ret['comment'] = 'Global vrouter config {0} has been created'.format(name)
+        ret['changes']['GlobalVRouterConfig'] = result
+    return ret
+
+
+def global_vrouter_config_absent(name, **kwargs):
+    '''
+    Ensure that the Contrail global vrouter config doesn't exist
+
+    :param name: The name of the global vrouter config that should not exist
+    '''
+    ret = {'name': name,
+           'changes': {},
+           'result': True,
+           'comment': 'Global vrouter config "{0}" is already absent'.format(name)}
+    vrouter_conf = __salt__['contrail.global_vrouter_config_get'](name, **kwargs)
+    if 'Error' not in vrouter_conf:
+        result = __salt__['contrail.global_vrouter_config_delete'](name, **kwargs)
+        ret['comment'] = 'Global vrouter config {0} has been deleted'.format(name)
+        ret['changes']['GlobalVRouterConfig'] = result
     return ret
 
 
@@ -183,11 +482,14 @@
            '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)
+
+    result = __salt__['contrail.linklocal_service_create'](name, lls_ip, lls_port, ipf_addresses, ipf_port, **kwargs)
+    if 'OK' in result:
+        ret['comment'] = result
+        pass
+    else:
         ret['comment'] = 'Link local service "{0}" has been created'.format(name)
-        ret['changes']['LinkLocalService'] = 'Created'
+        ret['changes']['LinkLocalService'] = result
     return ret
 
 
@@ -203,12 +505,12 @@
            '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)
+        result = __salt__['contrail.linklocal_service_delete'](name, **kwargs)
         ret['comment'] = 'Link local service "{0}" has been deleted'.format(name)
-        ret['changes']['LinkLocalService'] = 'Deleted'
-
+        ret['changes']['LinkLocalService'] = result
     return ret
 
+
 def analytics_node_present(name, ip_address, **kwargs):
     '''
     Ensures that the Contrail analytics node exists.
@@ -219,13 +521,14 @@
            'changes': {},
            'result': True,
            'comment': 'Analytics node {0} already exists'.format(name)}
-    analytics_node = __salt__['contrail.analytics_node_get'](name, **kwargs)
-    if 'Error' not in analytics_node:
+
+    result = __salt__['contrail.analytics_node_create'](name, ip_address, **kwargs)
+    if 'OK' in result:
+        ret['comment'] = result
         pass
     else:
-        __salt__['contrail.analytics_node_create'](name, ip_address, **kwargs)
         ret['comment'] = 'Analytics node {0} has been created'.format(name)
-        ret['changes']['AnalyticsNode'] = 'Created'
+        ret['changes']['AnalyticsNode'] = result
     return ret
 
 
@@ -239,13 +542,14 @@
            'changes': {},
            'result': True,
            'comment': 'Config node {0} already exists'.format(name)}
-    config_node = __salt__['contrail.config_node_get'](name, **kwargs)
-    if 'Error' not in config_node:
+    result = __salt__['contrail.config_node_create'](name, ip_address, **kwargs)
+
+    if 'OK' in result:
+        ret['comment'] = result
         pass
     else:
-        __salt__['contrail.config_node_create'](name, ip_address, **kwargs)
         ret['comment'] = 'Config node {0} has been created'.format(name)
-        ret['changes']['ConfigNode'] = 'Created'
+        ret['changes']['ConfigNode'] = result
     return ret
 
 
@@ -259,13 +563,14 @@
            'changes': {},
            'result': True,
            'comment': 'BGP router {0} already exists'.format(name)}
-    bgp_router = __salt__['contrail.bgp_router_get'](name, **kwargs)
-    if 'Error' not in bgp_router:
+
+    result = __salt__['contrail.bgp_router_create'](name, type, ip_address, asn, **kwargs)
+    if 'OK' in result:
+        ret['comment'] = result
         pass
     else:
-        __salt__['contrail.bgp_router_create'](name, type, ip_address, asn, **kwargs)
         ret['comment'] = 'BGP router {0} has been created'.format(name)
-        ret['changes']['BgpRouter'] = 'Created'
+        ret['changes']['BgpRouter'] = result
     return ret
 
 
@@ -279,11 +584,12 @@
            'changes': {},
            'result': True,
            'comment': 'Database node {0} already exists'.format(name)}
-    database_node = __salt__['contrail.database_node_get'](name, **kwargs)
-    if 'Error' not in database_node:
+
+    result = __salt__['contrail.database_node_create'](name, ip_address, **kwargs)
+    if 'OK' in result:
+        ret['coment'] = result
         pass
     else:
-        __salt__['contrail.database_node_create'](name, ip_address, **kwargs)
         ret['comment'] = 'Database node {0} has been created'.format(name)
-        ret['changes']['DatabaseNode'] = 'Created'
+        ret['changes']['DatabaseNode'] = result
     return ret
diff --git a/opencontrail/client.sls b/opencontrail/client.sls
index 35fe914..99b5bee 100644
--- a/opencontrail/client.sls
+++ b/opencontrail/client.sls
@@ -21,6 +21,7 @@
 opencontrail_client_virtual_router_{{ virtual_router_name }}:
   contrail.virtual_router_present:
   - name: {{ virtual_router.get('name', virtual_router_name) }}
+  - router_type: {{ virtual_router.get('router_type', virtual_router_name)}}
   - ip_address: {{ virtual_router.ip_address }}
   - dpdk_enabled: {{ virtual_router.get('dpdk_enabled', False) }}
   - user: {{ client.identity.user }}
@@ -33,6 +34,21 @@
 
 {%- endfor %}
 
+{%- if pillar.opencontrail.get('compute',{}).get('tor', {}).get('enabled', False) %}
+
+{%- for tor_name, tor in pillar.opencontrail.compute.tor.get('agent', {}).items() %}
+
+opencontrail_client_tor_router_{{ tor_name }}:
+  contrail.virtual_router_present:
+  - name: {{ pillar.linux.system.name }}-{{ tor.id }}
+  - router_type: tor-agent
+  - ip_address: {{ tor.address }}
+
+{%- endfor %}
+
+{%- endif %}
+
+
 {%- for config_node_name, config_node in client.get('config_node', {}).items() %}
 
 opencontrail_client_config_node_{{ config_node_name }}:
diff --git a/opencontrail/files/3.0/vnc_api_lib.ini b/opencontrail/files/3.0/vnc_api_lib.ini
index 072dc73..9795760 100644
--- a/opencontrail/files/3.0/vnc_api_lib.ini
+++ b/opencontrail/files/3.0/vnc_api_lib.ini
@@ -1,10 +1,12 @@
 {%- from "opencontrail/map.jinja" import config with context %}
+
 [global]
 ;WEB_SERVER = 127.0.0.1
 ;WEB_PORT = 9696  ; connection through quantum plugin
 
-WEB_SERVER = 127.0.0.1
-WEB_PORT = 8082 ; connection to api-server directly
+WEB_SERVER = {{ config.bind.address }}
+WEB_PORT = {{ config.bind.get('api_port', '8082') }}
+; connection to api-server directly
 BASE_URL = /
 ;BASE_URL = /tenants/infra ; common-prefix for all URLs
 
@@ -15,10 +17,14 @@
 AUTHN_PROTOCOL = http
 AUTHN_SERVER= {{ config.identity.host }}
 AUTHN_PORT = {{ config.identity.port }}
+AUTHN_TENANT = {{ config.identity.tenant }}
+AUTHN_USER =  {{ config.identity.user }}
+AUTHN_PASSWORD = {{ config.identity.password }}
 {%- if config.identity.version == "3" %}
 AUTHN_URL = /v3/auth/tokens
 {%- else %}
 AUTHN_URL = /v2.0/tokens
 {%- endif %}
 
+
 {%- endif %}
diff --git a/opencontrail/files/4.0/vnc_api_lib.ini b/opencontrail/files/4.0/vnc_api_lib.ini
index f131ab2..9795760 100644
--- a/opencontrail/files/4.0/vnc_api_lib.ini
+++ b/opencontrail/files/4.0/vnc_api_lib.ini
@@ -1,10 +1,12 @@
 {%- from "opencontrail/map.jinja" import config with context %}
+
 [global]
 ;WEB_SERVER = 127.0.0.1
 ;WEB_PORT = 9696  ; connection through quantum plugin
 
-WEB_SERVER = 127.0.0.1
-WEB_PORT = 8082 ; connection to api-server directly
+WEB_SERVER = {{ config.bind.address }}
+WEB_PORT = {{ config.bind.get('api_port', '8082') }}
+; connection to api-server directly
 BASE_URL = /
 ;BASE_URL = /tenants/infra ; common-prefix for all URLs
 
@@ -15,10 +17,14 @@
 AUTHN_PROTOCOL = http
 AUTHN_SERVER= {{ config.identity.host }}
 AUTHN_PORT = {{ config.identity.port }}
+AUTHN_TENANT = {{ config.identity.tenant }}
+AUTHN_USER =  {{ config.identity.user }}
+AUTHN_PASSWORD = {{ config.identity.password }}
 {%- if config.identity.version == "3" %}
 AUTHN_URL = /v3/auth/tokens
 {%- else %}
 AUTHN_URL = /v2.0/tokens
 {%- endif %}
 
-{%- endif %}
\ No newline at end of file
+
+{%- endif %}