PROD-18932

Change-Id: Icdfc04a62c49027db650820f3e625efcb9a749d9
diff --git a/README.rst b/README.rst
index 2f85ea0..5a7429e 100644
--- a/README.rst
+++ b/README.rst
@@ -169,6 +169,39 @@
        sshprefs:
         - 'ssh-rsa ASD.........dfsadf blah@blah'
 
+Multiple ip ranges for one particular subnet.
+
+.. code-block:: yaml
+
+  maas:
+    region:
+      subnets:
+        Subnet1:
+          cidr: 10.10.0.0/16
+          fabric: fabric-5
+          gateway_ip: 10.10.0.1
+          iprange:
+            start: 10.10.191.241
+            end: 10.10.255.244
+            type: reserved
+        Subnet2:
+          cidr: 130.10.0.0/16
+          fabric: fabric-6
+          gateway_ip: 130.10.0.1
+          multiple: True
+          iprange:
+            range1:
+              start: 130.10.0.10
+              end: 130.10.0.15
+              type: dynamic
+              comment: 'Coment 1'
+            range2:
+              start: 130.10.0.16
+              end: 130.10.0.20
+              type: reserved
+              comment: 'Comment 2'
+
+
 Update Vlan
 
 NOTE: Vid 0 has default name untagged in MaaS UI
diff --git a/_modules/maasng.py b/_modules/maasng.py
index dc8527b..54f15ae 100644
--- a/_modules/maasng.py
+++ b/_modules/maasng.py
@@ -99,8 +99,8 @@
 
     if not maas_volname:
         # MAAS-like name
-        volume_name = str("%s-%s" % (volume_group,volume_name))
-    ##TODO validation
+        volume_name = str("%s-%s" % (volume_group, volume_name))
+    # TODO validation
     return get_volumes(hostname, volume_group)[volume_name]["id"]
 
 
@@ -233,8 +233,9 @@
     raids = {}
     maas = _create_maas_client()
     system_id = get_machine(hostname)["system_id"]
-    #TODO validation
-    json_res = json.loads(maas.get(u"api/2.0/nodes/{0}/raids/".format(system_id)).read())
+    # TODO validation
+    json_res = json.loads(
+        maas.get(u"api/2.0/nodes/{0}/raids/".format(system_id)).read())
     LOG.debug('list_raids:{} {}'.format(system_id, json_res))
     for item in json_res:
         raids[item["name"]] = item
@@ -271,11 +272,12 @@
         salt-call maasng.delete_raid server_hostname raid_name
     """
     result = {}
-    maas=_create_maas_client()
+    maas = _create_maas_client()
     system_id = get_machine(hostname)["system_id"]
     raid_id = _get_raid_id_by_name(hostname, raid_name)
-    LOG.debug('delete_raid: {} {}'.format(system_id,raid_id))
-    maas.delete(u"api/2.0/nodes/{0}/raid/{1}/".format(system_id, raid_id)).read()
+    LOG.debug('delete_raid: {} {}'.format(system_id, raid_id))
+    maas.delete(
+        u"api/2.0/nodes/{0}/raid/{1}/".format(system_id, raid_id)).read()
 
     result["new"] = "Raid {0} deleted".format(raid_name)
     return result
@@ -497,7 +499,7 @@
 # DISK LAYOUT
 
 
-def drop_storage_schema(hostname,disk=None):
+def drop_storage_schema(hostname, disk=None):
     """
     #1. Drop lv
     #2. Drop vg
@@ -507,9 +509,10 @@
 
     if __opts__['test']:
         ret['result'] = None
-        ret['comment'] = 'Storage schema on {0} will be removed'.format(hostname)
+        ret['comment'] = 'Storage schema on {0} will be removed'.format(
+            hostname)
         return ret
-    #TODO validation if exists
+    # TODO validation if exists
     vgs = list_volume_groups(hostname)
     for vg in vgs:
         delete_volume_group(hostname, vg)
@@ -523,7 +526,8 @@
         partitions = __salt__['maasng.list_partitions'](hostname, block_d)
         for partition_name, partition in partitions.iteritems():
             LOG.info('delete partition:\n{}'.format(partition))
-            __salt__['maasng.delete_partition_by_id'](hostname, block_d, partition["id"])
+            __salt__['maasng.delete_partition_by_id'](
+                hostname, block_d, partition["id"])
 
 
 def update_disk_layout(hostname, layout, root_size=None, root_device=None, volume_group=None, volume_name=None, volume_size=None):
@@ -591,6 +595,7 @@
 # END DISK LAYOUT
 # LVM
 
+
 def list_volume_groups(hostname):
     """
     Get list of all volume group on machine.
@@ -709,10 +714,11 @@
 
     vg_id = str(_get_volume_group_id_by_name(hostname, name))
     for vol in get_volumes(hostname, name):
-        delete_volume(hostname,vol,name)
+        delete_volume(hostname, vol, name)
 
-    #TODO validation
-    json_res = json.loads(maas.delete(u"api/2.0/nodes/{0}/volume-group/{1}/".format(system_id, vg_id)).read() or 'null')
+    # TODO validation
+    json_res = json.loads(maas.delete(
+        u"api/2.0/nodes/{0}/volume-group/{1}/".format(system_id, vg_id)).read() or 'null')
     LOG.info(json_res)
 
     return True
@@ -754,7 +760,8 @@
     LOG.info(json_res)
 
     if fs_type != None or mount != None:
-        ret = create_volume_filesystem(hostname, volume_group + "-" + volume_name, fs_type, mount)
+        ret = create_volume_filesystem(
+            hostname, volume_group + "-" + volume_name, fs_type, mount)
 
     return True
 
@@ -774,12 +781,13 @@
         salt-call maasng.delete_volume server_hostname volume_name volume_group
     """
 
-    maas=_create_maas_client()
+    maas = _create_maas_client()
     system_id = get_machine(hostname)["system_id"]
     LOG.debug('delete_volume:{}'.format(system_id))
 
     volume_group_id = str(_get_volume_group_id_by_name(hostname, volume_group))
-    volume_id = str(_get_volume_id_by_name(hostname, volume_name, volume_group))
+    volume_id = str(_get_volume_id_by_name(
+        hostname, volume_name, volume_group))
 
     if None in [volume_group_id, volume_id]:
         return False
@@ -788,8 +796,9 @@
         "id": volume_id,
     }
 
-    #TODO validation
-    json_res = json.loads(maas.post(u"api/2.0/nodes/{0}/volume-group/{1}/".format(system_id, volume_group_id), "delete_logical_volume", **data).read() or 'null')
+    # TODO validation
+    json_res = json.loads(maas.post(u"api/2.0/nodes/{0}/volume-group/{1}/".format(
+        system_id, volume_group_id), "delete_logical_volume", **data).read() or 'null')
     return True
 
 
@@ -798,7 +807,8 @@
     Get list of volumes in volume group.
     """
     volumes = {}
-    _volumes = list_volume_groups(hostname)[vg_name].get('logical_volumes', False)
+    _volumes = list_volume_groups(
+        hostname)[vg_name].get('logical_volumes', False)
     if _volumes:
         for item in _volumes:
             volumes[item["name"]] = item
@@ -897,7 +907,7 @@
 
     maas = _create_maas_client()
     json_res = json.loads(maas.post(u"api/2.0/fabrics/", None, **data).read())
-    LOG.info(json_res)
+    LOG.debug("crete_fabric:{}".format(json_res))
     result["new"] = "Fabrics {0} created".format(json_res["name"])
     return result
 
@@ -933,7 +943,7 @@
     """
     vlans = {}
     maas = _create_maas_client()
-    fabric_id = get_fabric(fabric)
+    fabric_id = get_fabricid(fabric)
 
     json_res = json.loads(
         maas.get(u'api/2.0/fabrics/{0}/vlans/'.format(fabric_id)).read())
@@ -943,7 +953,7 @@
     return vlans
 
 
-def get_fabric(fabric):
+def get_fabricid(fabric):
     """
     Get id for specific fabric
 
@@ -951,7 +961,7 @@
 
     .. code-block:: bash
 
-        salt-call maasng.get_fabric fabric_name
+        salt 'maas-node' maasng.get_fabricid fabric_name
     """
     try:
         return list_fabric()[fabric]['id']
@@ -962,11 +972,8 @@
 def update_vlan(name, fabric, vid, description, primary_rack, dhcp_on=False):
     """
     Update vlan
-
     CLI Example:
-
     .. code-block:: bash
-
         salt 'maas-node' maasng.update_vlan name, fabric, vid, description, dhcp_on
     """
     result = {}
@@ -978,7 +985,7 @@
         "primary_rack": primary_rack,
     }
     maas = _create_maas_client()
-    fabric_id = get_fabric(fabric)
+    fabric_id = get_fabricid(fabric)
 
     json_res = json.loads(maas.put(
         u'api/2.0/fabrics/{0}/vlans/{1}/'.format(fabric_id, vid), **data).read())
@@ -987,6 +994,164 @@
 
     return result
 
+
+def list_subnets():
+    """
+    Get list of subnet from maas server
+
+    CLI Example:
+
+    .. code-block:: bash
+
+        salt 'maas-node' maasng.list_subnet
+    """
+    subnets = {}
+    maas = _create_maas_client()
+    json_res = json.loads(maas.get(u'api/2.0/subnets/').read())
+    for item in json_res:
+        subnets[item["name"]] = item
+    return subnets
+
+
+def create_subnet(cidr, name, fabric, gateway_ip):
+    """
+    Create subnet
+
+    CLI Example:
+
+    .. code-block:: bash
+
+        salt 'maas-node' maasng.create_subnet cidr, name, fabric, gateway_ip
+    """
+
+    fabric_id = get_fabricid(fabric)
+    result = {}
+
+    data = {
+        "cidr": cidr,
+        "name": name,
+        "fabric": str(fabric_id),
+        "gateway_ip": gateway_ip,
+    }
+    maas = _create_maas_client()
+
+    json_res = json.loads(maas.post(u"api/2.0/subnets/", None, **data).read())
+    LOG.debug("create_subnet:{}".format(json_res))
+    result["new"] = "Subnet {0} with CIDR {1} and gateway {2} was created".format(
+        name, cidr, gateway_ip)
+
+    return result
+
+
+def get_subnet(subnet):
+    """
+    Get details for specific subnet
+
+    CLI Example:
+
+    .. code-block:: bash
+
+        salt 'maas-node' maasng.get_subnet subnet_name
+    """
+    try:
+        return list_subnet()[subnet]
+    except KeyError:
+        return {"error": "Subnet not found on MaaS server"}
+
+
+def get_subnetid(subnet):
+    """
+    Get id for specific subnet
+
+    CLI Example:
+
+    .. code-block:: bash
+
+        salt 'maas-node' maasng.get_subnetid subnet_name
+    """
+    try:
+        return list_subnet()[subnet]['id']
+    except KeyError:
+        return {"error": "Subnet not found on MaaS server"}
+
+
+def list_ipranges():
+    """
+    Get list of all ipranges from maas server
+
+    CLI Example:
+
+    .. code-block:: bash
+
+        salt 'maas-node' maasng.list_ipranges
+    """
+    ipranges = {}
+    maas = _create_maas_client()
+    json_res = json.loads(maas.get(u'api/2.0/ipranges/').read())
+    for item in json_res:
+        ipranges[item["start_ip"]] = item
+    return ipranges
+
+
+def create_iprange(type_range, start_ip, end_ip, comment):
+    """
+    Create ip range
+
+    CLI Example:
+
+    .. code-block:: bash
+
+        salt 'maas-node' maasng.create_iprange type, start ip, end ip, comment
+    """
+    result = {}
+
+    data = {
+        "type": type_range,
+        "start_ip": start_ip,
+        "end_ip": end_ip,
+        "comment": comment,
+    }
+    maas = _create_maas_client()
+
+    json_res = json.loads(maas.post(u"api/2.0/ipranges/", None, **data).read())
+
+    LOG.debug("create_iprange:{}".format(json_res))
+    result["new"] = "Iprange with type {0}, start ip {1}, end ip {2}, was created".format(
+        type_range, start_ip, end_ip)
+
+    return result
+
+
+def get_iprangeid(start_ip):
+    """
+    Get id for ip range from maas server
+
+    CLI Example:
+
+    .. code-block:: bash
+
+        salt 'maas-node' maasng.get_iprangeid start_ip
+    """
+    try:
+        return list_ipranges()[start_ip]['id']
+    except KeyError:
+        return {"error": "Ip range not found on MaaS server"}
+
+
+def get_startip(start_ip):
+    """
+    Get start ip for ip range
+
+    CLI Example:
+
+    .. code-block:: bash
+
+        salt 'maas-node' maasng.get_startip start ip
+    """
+    try:
+        return list_ipranges()[start_ip]
+    except KeyError:
+        return {"error": "Ip range not found on MaaS server"}
 # END NETWORKING
 
 # MAAS CONFIG SECTION
@@ -1158,7 +1323,7 @@
             maas.get(u'api/2.0/boot-resources/', 'is_importing').read())
 
 #####
-#def boot_sources_selections_delete_all_others(except_urls=[]):
+# def boot_sources_selections_delete_all_others(except_urls=[]):
 #    """
 #    """
 #    result = {}
diff --git a/_states/maasng.py b/_states/maasng.py
index 1cc69f7..66987d2 100644
--- a/_states/maasng.py
+++ b/_states/maasng.py
@@ -1,4 +1,3 @@
-
 import logging
 from salt.exceptions import CommandExecutionError, SaltInvocationError
 
@@ -67,7 +66,8 @@
             hostname, layout_type, root_size, root_device, volume_group, volume_name, volume_size)
 
     elif layout_type == "custom":
-        ret["changes"] = __salt__['maasng.update_disk_layout'](hostname, layout_type)
+        ret["changes"] = __salt__[
+            'maasng.update_disk_layout'](hostname, layout_type)
 
     else:
         ret["comment"] = "Not supported layout provided. Choose flat or lvm"
@@ -436,3 +436,71 @@
                                                                       labels=labels,
                                                                       wait=wait)
     return ret
+
+
+def iprange_present(name, type_range, start_ip, end_ip, comment):
+    '''
+
+    :param name: Name of iprange
+    :param type_range: Type of iprange
+    :param start_ip: Start ip of iprange
+    :param end_ip: End ip of iprange
+    :param comment: Comment for specific iprange
+
+    '''
+
+    ret = {'name': name,
+           'changes': {},
+           'result': True,
+           'comment': 'Module function maasng.iprange_present executed'}
+
+    start = __salt__['maasng.get_startip'](start_ip)
+    if 'start_ip' in start.keys():
+        if start["start_ip"] == start_ip:
+            ret['comment'] = 'Iprange {0} already exist.'.format(name)
+            return ret
+
+    if __opts__['test']:
+        ret['result'] = None
+        ret['comment'] = 'Ip range {0} will be created with start ip: {1} and end ip: {2} and type {3}'.format(
+            name, start_ip, end_ip, type_range)
+        return ret
+
+    ret["changes"] = __salt__['maasng.create_iprange'](
+        type_range=type_range, start_ip=start_ip, end_ip=end_ip, comment=comment)
+
+    return ret
+
+
+def subnet_present(cidr, name, fabric, gateway_ip):
+    '''
+
+    :param cidr: Cidr for subnet
+    :param name: Name of subnet
+    :param fabric: Name of fabric for subnet
+    :param gateway_ip: gateway_ip
+
+    '''
+
+    ret = {'name': name,
+           'changes': {},
+           'result': True,
+           'comment': 'Module function maasng.subnet_present executed'}
+
+    subnet = __salt__['maasng.get_subnet'](name)
+    if 'name' in subnet.keys():
+        if subnet['name'] == name:
+            ret['comment'] = 'Subnet {0} already exist for fabric {1}'.format(
+                name, fabric)
+            return ret
+
+    if __opts__['test']:
+        ret['result'] = None
+        ret['comment'] = 'Subnet {0} will be created for {1}'.format(
+            name, fabric)
+        return ret
+
+    ret["changes"] = __salt__['maasng.create_subnet'](
+        cidr=cidr, name=name, fabric=fabric, gateway_ip=gateway_ip)
+
+    return ret
diff --git a/maas/region.sls b/maas/region.sls
index b0d9516..fc57871 100644
--- a/maas/region.sls
+++ b/maas/region.sls
@@ -279,15 +279,46 @@
     - cmd: maas_login_admin
 {%- endif %}
 
-{%- if region.get('subnets', False)  %}
-maas_subnets:
-  module.run:
-  - name: maas.process_subnets
+{%- if region.subnets is defined %}
+{%- for subnet_name, subnet in region.subnets.iteritems() %}
+maas_create_subnet_{{ subnet_name }}:
+  maasng.subnet_present:
+  - cidr: {{ subnet.cidr }}
+  - name: {{ subnet_name }}
+  - fabric: {{ subnet.fabric }}
+  - gateway_ip: {{ subnet.gateway_ip }}
   - require:
     - cmd: maas_login_admin
     {%- if region.get('fabrics', False)  %}
     - module: maas_fabrics
     {%- endif %}
+{%- endfor %}
+
+{%- for subnet_name, subnet in region.subnets.iteritems() %}
+{%- if subnet.get('multiple') == True %}
+{%- for range_name, iprange in subnet.get('iprange',{}).items() %}
+maas_create_ipranger_{{ range_name }}:
+  maasng.iprange_present:
+  - name: {{ range_name }}
+  - type_range: {{ iprange.type }}
+  - start_ip: {{ iprange.start }}
+  - end_ip: {{ iprange.end }}
+  - comment: {{ iprange.comment }}
+  - require:
+    - maas_create_subnet_{{ subnet_name }}
+{%- endfor %}
+{%- else %}
+maas_create_ipranger_{{ subnet_name }}:
+  maasng.iprange_present:
+  - name: {{ subnet.get('cidr', []) }}
+  - type_range: {{ subnet.iprange.type }}
+  - start_ip: {{ subnet.iprange.start }}
+  - end_ip: {{ subnet.iprange.end }}
+  - comment: {{ subnet.iprange.type }}
+  - require:
+    - maas_create_subnet_{{ subnet_name }}
+{%- endif %}
+{%- endfor %}
 {%- endif %}
 
 {%- if region.get('devices', False)  %}