Merge "Remove salt-master, reclass from pkg dependencis"
diff --git a/README.rst b/README.rst
index 5f09362..d465fac 100644
--- a/README.rst
+++ b/README.rst
@@ -984,6 +984,8 @@
           type: router
           ip_address: 172.16.0.22
           asn: 64512
+          key_type: md5
+          key: password
 
 Enforcing config nodes
 
@@ -1126,6 +1128,35 @@
                     security_group: 'default'
                     virtual_network: 'virtual-network'
 
+Enforcing virtual networks
+
+
+.. code-block:: yaml
+
+  opencontrail:
+    client:
+      virtual_networks:
+        net01:
+          name: 'network01'
+          ip_address: '172.16.111.0'
+          ip_prefix: 24
+          asn: 64512
+          route_target: 10000
+          external: True
+          allow_transit: False
+          forwarding_mode: 'l2_l3'
+          rpf: 'disable'
+          mirror_destination: False
+          domain: 'default-domain'
+          project: 'admin'
+          ipam_domain: 'default-domain'
+          ipam_project: 'default-project'
+          ipam_name: 'default-network-ipam'
+        net02:
+          name: 'network02'
+        net03:
+          name: 'network03'
+
 
 Contrail DNS custom forwarders
 ------------------------------
diff --git a/_modules/contrail.py b/_modules/contrail.py
index 38281b2..df33d78 100644
--- a/_modules/contrail.py
+++ b/_modules/contrail.py
@@ -24,9 +24,11 @@
     from vnc_api.vnc_api import LinklocalServiceEntryType, \
         LinklocalServicesTypes, GlobalVrouterConfig, GlobalSystemConfig
     from vnc_api.gen.resource_client import VirtualRouter, AnalyticsNode, \
-        ConfigNode, DatabaseNode, BgpRouter
+        ConfigNode, DatabaseNode, BgpRouter, VirtualNetwork
     from vnc_api.gen.resource_xsd import AddressFamilies, BgpSessionAttributes, \
-        BgpSession, BgpPeeringAttributes, BgpRouterParams
+        BgpSession, BgpPeeringAttributes, BgpRouterParams, AuthenticationData, \
+        AuthenticationKeyItem, VirtualNetworkType, IpamSubnetType, SubnetType, \
+        VnSubnetsType, RouteTargetList
 
     HAS_CONTRAIL = True
 except ImportError:
@@ -1014,7 +1016,7 @@
     return ret
 
 
-def bgp_router_create(name, type, ip_address, asn=64512, **kwargs):
+def bgp_router_create(name, type, ip_address, asn=64512, key_type=None, key=None, **kwargs):
     '''
     Create specific Contrail control node
 
@@ -1036,6 +1038,9 @@
     if type != 'control-node':
         address_families.remove('erm-vpn')
 
+    key_type = None if key_type == 'None' else key_type
+    key = None if key == 'None' else key
+
     bgp_addr_fams = AddressFamilies(address_families)
     bgp_sess_attrs = [
        BgpSessionAttributes(address_families=bgp_addr_fams)]
@@ -1043,10 +1048,16 @@
     bgp_peering_attrs = BgpPeeringAttributes(session=bgp_sessions)
     rt_inst_obj = _get_rt_inst_obj(vnc_client)
 
+    bgp_auth_data = None
+
     if type == 'control-node':
         vendor = 'contrail'
     elif type == 'router':
         vendor = 'mx'
+        if key_type == 'md5':
+            key_id = 0
+            key_items = AuthenticationKeyItem(key_id, key)
+            bgp_auth_data = AuthenticationData(key_type, [key_items])
     else:
         vendor = 'unknown'
 
@@ -1054,7 +1065,8 @@
                                     vendor=vendor, autonomous_system=int(asn),
                                     identifier=_get_ip(ip_address),
                                     address=_get_ip(ip_address),
-                                    port=179, address_families=bgp_addr_fams)
+                                    port=179, address_families=bgp_addr_fams,
+                                    auth_data=bgp_auth_data)
 
     bgp_router_objs = bgp_router_list(**kwargs)
     if name in bgp_router_objs:
@@ -1066,6 +1078,18 @@
             ret['changes'].update({"vendor": {'old': bgp_router_obj.bgp_router_parameters.vendor, 'new': vendor}})
         if bgp_router_obj.bgp_router_parameters.address != ip_address:
             ret['changes'].update({"ip_address": {'old': bgp_router_obj.bgp_router_parameters.address, 'new': ip_address}})
+        try:
+            if bgp_router_obj.bgp_router_parameters.auth_data.key_type != key_type:
+                ret['changes'].update({"key_type": {'old': bgp_router_obj.bgp_router_parameters.auth_data.key_type, 'new': key_type}})
+        except:
+            if key_type != None:
+                ret['changes'].update({"key_type": {'old': None, 'new': key_type}})
+        if key_type == 'md5':
+            try:
+                if bgp_router_obj.bgp_router_parameters.auth_data.key_items[0].key != key:
+                    ret['changes'].update({"key_type": {'old': bgp_router_obj.bgp_router_parameters.auth_data.key_items[0].key, 'new': key}})
+            except:
+                ret['changes'].update({"key_type": {'old': None, 'new': key}})
 
         if len(ret['changes']) == 0:
             return ret
@@ -1524,6 +1548,142 @@
     return ret
 
 
+def virtual_network_create(name, conf=None, **kwargs):
+    '''
+    Create Contrail virtual network
+    CLI Example:
+    .. code-block:: bash
+    salt '*' contrail.virtual_network_create name
+
+    salt.cmdRun(pepperEnv, 'ntw01*', 'salt-call contrail.virtual_network_create
+    "testicek" "{"external":"True","ip":"172.16.111.0","prefix":24,
+    "asn":64512,"target":10000}" ')
+
+    Parameters:
+    name required - name of the new network
+
+    conf (dict) optional:
+        domain (string) optional - which domain use for vn creation
+        project (string) optional - which project use for vn creation
+        ipam_domain (string) optional - domain for ipam
+        ipam_project (string) optional - project for ipam
+        ipam_name (string) optional - ipam name
+        ip_prefix (string) optional - format is xxx.xxx.xxx.xxx
+        ip_prefix_len (int) optional - format is xx
+        asn (int) optional - autonomus system number
+        target (int) optional - route target number
+        external (boolean) optional - set if network is external
+
+        allow_transit (boolean) optional - enable allow transit
+        forwarding_mode (any of ['l2_l3','l2','l3']) optional
+            - packet forwarding mode for this virtual network
+        rpf (any of ['enabled','disabled']) optional
+            - Enable or disable Reverse Path Forwarding check
+        for this network
+        mirror_destination (boolean) optional
+            - Mark the vn as mirror destination network
+    '''
+    if conf is None:
+        conf = {}
+
+    # check for domain, is missing set to default-domain
+    if 'domain' in conf:
+        vn_domain = str(conf['domain'])
+    else:
+        vn_domain = 'default-domain'
+    # check for project, is missing set to admin
+    if 'project' in conf:
+        vn_project = str(conf['project'])
+    else:
+        vn_project = 'admin'
+    # check for ipam domain,default is default-domain
+    if 'ipam_domain' in conf:
+        ipam_domain = str(conf['ipam_domain'])
+    else:
+        ipam_domain = 'default-domain'
+    # check for ipam domain,default is default-domain
+    if 'ipam_project' in conf:
+        ipam_project = str(conf['ipam_project'])
+    else:
+        ipam_project = 'default-project'
+
+    if 'ipam_name' in conf:
+        ipam_name = conf['ipam_name']
+    else:
+        ipam_name = 'default-network-ipam'
+
+    ret = {'name': name,
+           'changes': {},
+           'result': True,
+           'comment': ''}
+
+    # list of existing vn networks
+    vn_networks = []
+    vnc_client = _auth(**kwargs)
+    prj_obj = vnc_client.project_read(fq_name=[vn_domain,
+                                               vn_project])
+    # check if the network exists
+    vn_networks_list = vnc_client._objects_list('virtual_network')
+    fq = [vn_domain, vn_project, name]
+    for network in vn_networks_list['virtual-networks']:
+        if fq == network['fq_name']:
+            ret['comment'] = ("Virtual network with name "
+                              + name + " already exists")
+            return ret
+
+    vn_obj = VirtualNetwork(name, prj_obj)
+    vn_type_obj = VirtualNetworkType()
+    # get ipam from default project and domain
+    ipam = vnc_client.network_ipam_read(fq_name=[ipam_domain,
+                                                 ipam_project,
+                                                 ipam_name])
+
+    # create subnet
+    if 'ip_prefix' in conf and 'ip_prefix_len' in conf:
+        ipam_subnet_type = IpamSubnetType(subnet=SubnetType(
+                                          ip_prefix=conf['ip_prefix'],
+                                          ip_prefix_len=conf['ip_prefix_len']))
+
+        vn_subnets_type_obj = VnSubnetsType(ipam_subnets=[ipam_subnet_type])
+        vn_obj.add_network_ipam(ipam, vn_subnets_type_obj)
+
+    # add route target to the network
+    if 'asn' in conf and 'target' in conf:
+        route_target_list_obj = RouteTargetList(["target:{0}:{1}"
+                                                 .format(conf['asn'],
+                                                         conf['target'])])
+        vn_obj.set_route_target_list(route_target_list_obj)
+
+    if 'external' in conf:
+        vn_obj.set_router_external(conf['external'])
+
+    if 'allow_transit' in conf:
+        vn_type_obj.set_allow_transit(conf['allow_transit'])
+
+    if 'forwarding_mode' in conf:
+        if conf['forwarding_mode'] in ['l2_l3', 'l2', 'l3']:
+            vn_type_obj.set_forwarding_mode(conf['forwarding_mode'])
+
+    if 'rpf' in conf:
+        vn_type_obj.set_rpf(conf['rpf'])
+
+    if 'mirror_destination' in conf:
+        vn_type_obj.set_mirror_destination(conf['mirror_destination'])
+
+    vn_obj.set_virtual_network_properties(vn_type_obj)
+
+    # create virtual network
+    if __opts__['test']:
+        ret['result'] = None
+        ret['comment'] = ("Virtual network with name {0} will be created"
+                          .format(name))
+    else:
+        vnc_client.virtual_network_create(vn_obj)
+        ret['comment'] = ("Virtual network with name {0} was created"
+                          .format(name))
+    return ret
+
+
 def service_appliance_set_list(**kwargs):
     '''
     Return a list of Contrail service appliance set
diff --git a/_states/contrail.py b/_states/contrail.py
index ea74ce3..b729d26 100644
--- a/_states/contrail.py
+++ b/_states/contrail.py
@@ -251,6 +251,8 @@
         - ip_address: 10.0.0.133
         - type: mx
         - asn: 64512
+        - key_type: md5
+        - key: password
 
 
 Enforce the BGP router absence
@@ -336,6 +338,31 @@
     global_system_config_delete:
       contrail.global_system_config_absent:
         - name: global-system_config
+
+
+Enforce the virtual network existence
+----------------------------------------
+
+.. code-block: yaml
+
+    virtual_network_create:
+      contrail.virtual_network_present:
+        - name: virtual_network_name
+        - conf:
+            domain: domain name
+            project: domain project
+            ipam_domain: ipam domain name
+            ipam_project: ipam project name
+            ipam_name: ipam name
+            ip_prefix: xxx.xxx.xxx.xxx
+            ip_prefix_len: 24
+            asn: 64512
+            target: 10000
+            external: False
+            allow_transit: False
+            forwading_mode: 'l2_l3'
+            rpf: 'disabled'
+            mirror_destination: False
 '''
 
 
@@ -623,7 +650,7 @@
     return ret
 
 
-def bgp_router_present(name, type, ip_address, asn=64512, **kwargs):
+def bgp_router_present(name, type, ip_address, asn=64512, key_type=None, key=None, **kwargs):
     '''
     Ensures that the Contrail BGP router exists.
 
@@ -634,7 +661,7 @@
            'result': True,
            'comment': 'BGP router {0} already exists'.format(name)}
 
-    ret = __salt__['contrail.bgp_router_create'](name, type, ip_address, asn, **kwargs)
+    ret = __salt__['contrail.bgp_router_create'](name, type, ip_address, asn, key_type, key, **kwargs)
     if len(ret['changes']) == 0:
         pass
     return ret
@@ -790,3 +817,15 @@
     if 'Error' not in gsc:
         ret = __salt__['contrail.global_system_config_delete'](name, **kwargs)
     return ret
+
+
+def virtual_network_present(name, conf=None, **kwargs):
+    '''
+    Ensure that the virtual network exists.
+
+    :param name: Name of the virtual network
+    :param conf: Key:Value pairs used for network creation
+    '''
+
+    ret = __salt__['contrail.virtual_network_create'](name, conf, **kwargs)
+    return ret
diff --git a/opencontrail/client.sls b/opencontrail/client.sls
index 24370ac..ff67a3e 100644
--- a/opencontrail/client.sls
+++ b/opencontrail/client.sls
@@ -95,6 +95,8 @@
   - ip_address: {{ bgp_router.ip_address }}
   - type: {{ bgp_router.type }}
   - asn: {{ bgp_router.get('asn', 64512) }}
+  - key_type: {{ bgp_router.get('key_type') }}
+  - key: {{ bgp_router.get('key') }}
   - user: {{ client.identity.user }}
   - password: {{ client.identity.password }}
   - project: {{ client.identity.tenant }}
@@ -234,4 +236,63 @@
 
 {%- endfor %} # end for physical_router
 
+{%- if client.virtual_networks is defined %}
+{%- for vn_name, vn in client.virtual_networks.items() %}
+create_network_{{ vn.name }}:
+  contrail.virtual_network_present:
+  - name: {{ vn.name }}
+  - conf:
+{%- if vn.ip_prefix is defined and vn.ip_prefix_len is defined %}
+      ip: {{ vn.ip_prefix }}
+      prefix: {{ vn.ip_prefix_len }}
+{%- endif %}
+
+{%- if vn.asn is defined and vn.route_target is defined %}
+      asn: {{ vn.asn }}
+      target: {{ vn.route_target }}
+{%- endif %}
+
+{%- if vn.external is defined %}
+      external: {{ vn.external }}
+{%- endif %}
+
+{%- if vn.allow_transit is defined %}
+      allow_transit: {{ vn.allow_transit }}
+{%- endif %}
+
+{%- if vn.forwarding_mode is defined %}
+      forwarding_mode: {{ vn.forwarding_mode }}
+{%- endif %}
+
+{%- if vn.rpf is defined %}
+      rpf: {{ vn.rpf }}
+{%- endif %}
+
+{%- if vn.mirror_destination is defined %}
+      mirror_destination: {{ vn.mirror_destination }}
+{%- endif %}
+
+{%- if vn.domain is defined %}
+      domain: {{ vn.domain }}
+{%- endif %}
+
+{%- if vn.project is defined %}
+      project: {{ vn.project }}
+{%- endif %}
+
+{%- if vn.ipam_domain is defined %}
+      ipam_domain: {{ vn.ipam_domain }}
+{%- endif %}
+
+{%- if vn.ipam_project is defined %}
+      ipam_project: {{ vn.ipam_project }}
+{%- endif %}
+
+{%- if vn.ipam_name is defined %}
+      ipam_name: {{ vn.ipam_name }}
+{%- endif %}
+
+{%- endfor %}
+{%- endif %}  # end for virtual_network
+
 {%- endif %}