Merge pull request #30 from salt-formulas/update-readme

Update readme with new examples and fixes
diff --git a/README.rst b/README.rst
index 22409f3..e466959 100644
--- a/README.rst
+++ b/README.rst
@@ -169,6 +169,24 @@
        sshprefs:
         - 'ssh-rsa ASD.........dfsadf blah@blah'
 
+Update Vlan
+
+NOTE: Vid 0 has default name untagged in MaaS UI
+
+.. code-block:: yaml
+
+  maas:
+    region:
+      fabrics:
+        test-fabric:
+          description: "Test fabric"
+          vlan:
+            0:
+              description: "Your VLAN 0"
+              dhcp: True
+            13:
+              description: "Your VLAN 13"
+              dhcp: False
 
 Create disk schema per machine via maas/client.sls with default lvm schema + default values
 
diff --git a/_modules/maas.py b/_modules/maas.py
index 9ba76e5..426aff5 100644
--- a/_modules/maas.py
+++ b/_modules/maas.py
@@ -218,7 +218,8 @@
     def fill_data(self, name, subnet, fabrics):
         data = {
             'name': name,
-            'fabric': str(fabrics[subnet.get('fabric', '')]),
+            'fabric': str(fabrics[subnet.get('fabric',
+                self._get_fabric_from_cidr(subnet.get('cidr')))]),
             'cidr': subnet.get('cidr'),
             'gateway_ip': subnet['gateway_ip'],
         }
@@ -235,6 +236,13 @@
         self._process_iprange(res_json['id'])
         return response
 
+    def _get_fabric_from_cidr(self, cidr):
+        subnets = json.loads(self._maas.get(u'api/2.0/subnets/').read())
+        for subnet in subnets:
+            if subnet['cidr'] == cidr:
+                return subnet['vlan']['fabric']
+        return ''
+
     def _process_iprange(self, subnet_id):
         ipranges = json.loads(self._maas.get(u'api/2.0/ipranges/').read())
         LOG.warn('all %s ipranges %s', subnet_id, ipranges)
diff --git a/_modules/maasng.py b/_modules/maasng.py
index f10b3bc..1939b80 100644
--- a/_modules/maasng.py
+++ b/_modules/maasng.py
@@ -22,7 +22,7 @@
 import os.path
 import time
 import urllib2
-#Salt utils
+# Salt utils
 from salt.exceptions import CommandExecutionError, SaltInvocationError
 
 LOG = logging.getLogger(__name__)
@@ -61,13 +61,15 @@
 
 APIKEY_FILE = '/var/lib/maas/.maas_credentials'
 
+
 def _format_data(data):
     class Lazy:
         def __str__(self):
             return ' '.join(['{0}={1}'.format(k, v)
-                            for k, v in data.iteritems()])
+                             for k, v in data.iteritems()])
     return Lazy()
 
+
 def _create_maas_client():
     global APIKEY_FILE
     try:
@@ -80,22 +82,26 @@
     dispatcher = MAASDispatcher()
     return MAASClient(auth, dispatcher, api_url)
 
-def _get_blockdevice_id_by_name(hostname,device):
 
-    ##TODO validation
+def _get_blockdevice_id_by_name(hostname, device):
+
+    # TODO validation
     return list_blockdevices(hostname)[device]["id"]
 
-def _get_volume_group_id_by_name(hostname,device):
 
-    ##TODO validation
+def _get_volume_group_id_by_name(hostname, device):
+
+    # TODO validation
     return list_volume_groups(hostname)[device]["id"]
 
+
 def _get_partition_id_by_name(hostname, device, partition):
 
-    ##TODO validation
+    # TODO validation
     return list_partitions(hostname, device)[partition]["id"]
 
-####MACHINE SECTION
+# MACHINE SECTION
+
 
 def get_machine(hostname):
     '''
@@ -112,6 +118,7 @@
     except KeyError:
         return {"error": "Machine not found on MaaS server"}
 
+
 def list_machines():
     '''
     Get list of all machines from maas server
@@ -122,27 +129,30 @@
 
         salt 'maas-node' maasng.list_machines
     '''
-    machines={}
-    maas=_create_maas_client()
+    machines = {}
+    maas = _create_maas_client()
     json_res = json.loads(maas.get(u'api/2.0/machines/').read())
     for item in json_res:
         machines[item["hostname"]] = item
     return machines
 
+
 def create_machine():
-    ##TODO
+    # TODO
 
     return False
 
+
 def update_machine():
-    ##TODO
+    # TODO
 
     return False
 
-####MACHINE OPERATIONS
-##TODO
+# MACHINE OPERATIONS
+# TODO
 
-####RAID SECTION
+# RAID SECTION
+
 
 def create_raid(hostname, name, level, disks=[], partitions=[], **kwargs):
     '''
@@ -165,18 +175,20 @@
 
     for disk in disks:
         try:
-            disk_ids.append(str(_get_blockdevice_id_by_name(hostname,disk)))
+            disk_ids.append(str(_get_blockdevice_id_by_name(hostname, disk)))
         except KeyError:
-            result["error"] = "Device {0} does not exists on machine {1}".format(disk, hostname)
+            result["error"] = "Device {0} does not exists on machine {1}".format(
+                disk, hostname)
             return result
 
     for partition in partitions:
         try:
             device = partition.split("-")[0]
-            device_part = list_partitions(hostname,device)
+            device_part = list_partitions(hostname, device)
             partition_ids.append(str(device_part[partition]["id"]))
         except KeyError:
-            result["error"] = "Partition {0} does not exists on machine {1}".format(partition,hostname)
+            result["error"] = "Partition {0} does not exists on machine {1}".format(
+                partition, hostname)
             return result
 
     data = {
@@ -186,18 +198,20 @@
         "partitions": partition_ids,
     }
 
-    maas=_create_maas_client()
+    maas = _create_maas_client()
     system_id = get_machine(hostname)["system_id"]
     LOG.info(system_id)
 
-    #TODO validation
+    # TODO validation
     LOG.info(data)
-    json_res = json.loads(maas.post(u"api/2.0/nodes/{0}/raids/".format(system_id), None, **data).read())
+    json_res = json.loads(
+        maas.post(u"api/2.0/nodes/{0}/raids/".format(system_id), None, **data).read())
     LOG.info(json_res)
     result["new"] = "Raid {0} created".format(name)
 
     return result
 
+
 def list_raids(hostname):
     '''
     Get list all raids on machine
@@ -209,16 +223,18 @@
         salt-call maasng.list_raids server_hostname
     '''
 
-    maas=_create_maas_client()
+    maas = _create_maas_client()
     system_id = get_machine(hostname)["system_id"]
     LOG.info(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.info(json_res)
 
-    ##TODO return list of raid devices
+    # TODO return list of raid devices
     return True
 
+
 def get_raid(hostname, name):
     '''
     Get information about specific raid on machine
@@ -233,7 +249,7 @@
     return list_raids(hostname)[name]
 
 
-####BLOCKDEVICES SECTION
+# BLOCKDEVICES SECTION
 
 def list_blockdevices(hostname):
     '''
@@ -248,19 +264,21 @@
     '''
     ret = {}
 
-    maas=_create_maas_client()
+    maas = _create_maas_client()
     system_id = get_machine(hostname)["system_id"]
     LOG.info(system_id)
 
-    #TODO validation if exists
+    # TODO validation if exists
 
-    json_res = json.loads(maas.get(u"api/2.0/nodes/{0}/blockdevices/".format(system_id)).read())
+    json_res = json.loads(
+        maas.get(u"api/2.0/nodes/{0}/blockdevices/".format(system_id)).read())
     LOG.info(json_res)
     for item in json_res:
         ret[item["name"]] = item
 
     return ret
 
+
 def get_blockdevice(hostname, name):
     '''
     Get information about blockdevice (disk) on machine
@@ -276,7 +294,7 @@
     return list_blockdevices(hostname)[name]
 
 
-####PARTITIONS
+# PARTITIONS
 
 def list_partitions(hostname, device):
     '''
@@ -290,15 +308,15 @@
         salt-call maasng.list_partitions server_hostname sda
     '''
     ret = {}
-    maas=_create_maas_client()
+    maas = _create_maas_client()
     system_id = get_machine(hostname)["system_id"]
     LOG.info(system_id)
 
-    partitions = get_blockdevice(hostname,device)["partitions"]
+    partitions = get_blockdevice(hostname, device)["partitions"]
     LOG.info(partitions)
 
     #json_res = json.loads(maas.get(u"api/2.0/nodes/{0}/blockdevices/{1}/partitions/".format(system_id, device_id)).read())
-    #LOG.info(json_res)
+    # LOG.info(json_res)
 
     if len(device) > 0:
         for item in partitions:
@@ -307,6 +325,7 @@
 
     return ret
 
+
 def get_partition(hostname, device, partition):
     '''
     Get information about specific parition on device located on machine
@@ -323,6 +342,7 @@
 
     return list_partitions(partition)[name]
 
+
 def create_partition(hostname, disk, size, fs_type=None, mount=None):
     '''
     Create new partition on device.
@@ -334,13 +354,13 @@
         salt 'maas-node' maasng.create_partition server_hostname disk_name 10 ext4 "/"
         salt-call maasng.create_partition server_hostname disk_name 10 ext4 "/"
     '''
-    ##TODO validation
+    # TODO validation
     result = {}
-    maas=_create_maas_client()
+    maas = _create_maas_client()
     system_id = get_machine(hostname)["system_id"]
     LOG.info(system_id)
 
-    device_id = _get_blockdevice_id_by_name(hostname,disk)
+    device_id = _get_blockdevice_id_by_name(hostname, disk)
     LOG.info(device_id)
 
     value, unit = size[:-1], size[-1]
@@ -351,8 +371,9 @@
         "size": calc_size
     }
 
-    #TODO validation
-    partition = json.loads(maas.post(u"api/2.0/nodes/{0}/blockdevices/{1}/partitions/".format(system_id, device_id), None, **data).read())
+    # TODO validation
+    partition = json.loads(maas.post(
+        u"api/2.0/nodes/{0}/blockdevices/{1}/partitions/".format(system_id, device_id), None, **data).read())
     LOG.info(partition)
     result["partition"] = "Partition created on {0}".format(disk)
 
@@ -362,8 +383,9 @@
         }
         partition_id = str(partition["id"])
         LOG.info("Partition id: " + partition_id)
-        #TODO validation
-        json_res = json.loads(maas.post(u"api/2.0/nodes/{0}/blockdevices/{1}/partition/{2}".format(system_id, device_id, partition_id), "format", **data_fs_type).read())
+        # TODO validation
+        json_res = json.loads(maas.post(u"api/2.0/nodes/{0}/blockdevices/{1}/partition/{2}".format(
+            system_id, device_id, partition_id), "format", **data_fs_type).read())
         LOG.info(json_res)
         result["filesystem"] = "Filesystem {0} created".format(fs_type)
 
@@ -372,14 +394,15 @@
             "mount_point": mount
         }
 
-        #TODO validation
-        json_res = json.loads(maas.post(u"api/2.0/nodes/{0}/blockdevices/{1}/partition/{2}".format(system_id, device_id, str(partition['id'])), "mount", **data).read())
+        # TODO validation
+        json_res = json.loads(maas.post(u"api/2.0/nodes/{0}/blockdevices/{1}/partition/{2}".format(
+            system_id, device_id, str(partition['id'])), "mount", **data).read())
         LOG.info(json_res)
         result["mount"] = "Mount point {0} created".format(mount)
 
-
     return result
 
+
 def delete_partition(hostname, disk, partition_name):
     '''
     Delete partition on device.
@@ -395,19 +418,21 @@
     '''
     result = {}
     data = {}
-    maas=_create_maas_client()
+    maas = _create_maas_client()
     system_id = get_machine(hostname)["system_id"]
     LOG.info(system_id)
 
-    device_id = _get_blockdevice_id_by_name(hostname,disk)
+    device_id = _get_blockdevice_id_by_name(hostname, disk)
     LOG.info(device_id)
 
-    partition_id = _get_partition_id_by_name(hostname,disk,partition_name)
+    partition_id = _get_partition_id_by_name(hostname, disk, partition_name)
 
-    maas.delete(u"api/2.0/nodes/{0}/blockdevices/{1}/partition/{2}".format(system_id, device_id, partition_id)).read()
+    maas.delete(u"api/2.0/nodes/{0}/blockdevices/{1}/partition/{2}".format(
+        system_id, device_id, partition_id)).read()
     result["new"] = "Partition {0} deleted".format(partition_name)
     return result
 
+
 def delete_partition_by_id(hostname, disk, partition_id):
     '''
     Delete partition on device. Partition spefified by id of parition
@@ -423,19 +448,21 @@
     '''
     result = {}
     data = {}
-    maas=_create_maas_client()
+    maas = _create_maas_client()
     system_id = get_machine(hostname)["system_id"]
     LOG.info(system_id)
 
-    device_id = _get_blockdevice_id_by_name(hostname,disk)
+    device_id = _get_blockdevice_id_by_name(hostname, disk)
     LOG.info(device_id)
 
-    maas.delete(u"api/2.0/nodes/{0}/blockdevices/{1}/partition/{2}".format(system_id, device_id, partition_id)).read()
+    maas.delete(u"api/2.0/nodes/{0}/blockdevices/{1}/partition/{2}".format(
+        system_id, device_id, partition_id)).read()
     result["new"] = "Partition {0} deleted".format(partition_id)
     return result
 
-####CREATE DISK LAYOUT
-##TODO
+# CREATE DISK LAYOUT
+# TODO
+
 
 def update_disk_layout(hostname, layout, root_size=None, root_device=None, volume_group=None, volume_name=None, volume_size=None):
     '''
@@ -455,7 +482,7 @@
         "storage_layout": layout,
     }
 
-    maas=_create_maas_client()
+    maas = _create_maas_client()
     system_id = get_machine(hostname)["system_id"]
     LOG.info(system_id)
 
@@ -464,10 +491,10 @@
         LOG.info(bit_size)
         data["root_size"] = bit_size
 
-
     if root_device != None:
         LOG.info(root_device)
-        data["root_device"] = str(_get_blockdevice_id_by_name(hostname,root_device))
+        data["root_device"] = str(
+            _get_blockdevice_id_by_name(hostname, root_device))
 
     if layout == 'lvm':
         if volume_group != None:
@@ -481,9 +508,9 @@
             LOG.info(vol_size)
             data["lv_size"] = vol_size
 
-
-    #TODO validation
-    json_res = json.loads(maas.post(u"api/2.0/machines/{0}/".format(system_id), "set_storage_layout", **data).read())
+    # TODO validation
+    json_res = json.loads(maas.post(
+        u"api/2.0/machines/{0}/".format(system_id), "set_storage_layout", **data).read())
     LOG.info(json_res)
     result["new"] = {
         "storage_layout": layout,
@@ -492,8 +519,8 @@
     return result
 
 
-####LVM
-##TODO
+# LVM
+# TODO
 
 def list_volume_groups(hostname):
     '''
@@ -508,17 +535,18 @@
     '''
     volume_groups = {}
 
-    maas=_create_maas_client()
+    maas = _create_maas_client()
     system_id = get_machine(hostname)["system_id"]
     LOG.info(system_id)
 
-    #TODO validation if exists
+    # TODO validation if exists
 
-    json_res = json.loads(maas.get(u"api/2.0/nodes/{0}/volume-groups/".format(system_id)).read())
+    json_res = json.loads(
+        maas.get(u"api/2.0/nodes/{0}/volume-groups/".format(system_id)).read())
     LOG.info(json_res)
     for item in json_res:
         volume_groups[item["name"]] = item
-    #return
+    # return
     return volume_groups
 
 
@@ -533,7 +561,7 @@
         salt 'maas-node' maasng.list_blockdevices server_hostname
         salt-call maasng.list_blockdevices server_hostname
     '''
-    ##TODO validation that exists
+    # TODO validation that exists
     return list_volume_groups(hostname)[name]
 
 
@@ -554,7 +582,7 @@
         "name": volume_group_name,
     }
 
-    maas=_create_maas_client()
+    maas = _create_maas_client()
     system_id = get_machine(hostname)["system_id"]
     LOG.info(system_id)
 
@@ -566,16 +594,18 @@
         if p_disk["partition_table_type"] == None:
             disk_ids.append(str(p_disk["id"]))
         else:
-            result["error"] = "Device {0} on machine {1} cointains partition table".format(disk,hostname)
+            result["error"] = "Device {0} on machine {1} cointains partition table".format(
+                disk, hostname)
             return result
 
     for partition in partitions:
         try:
             device = partition.split("-")[0]
-            device_part = list_partitions(hostname,device)
+            device_part = list_partitions(hostname, device)
             partition_ids.append(str(device_part[partition]["id"]))
         except KeyError:
-            result["error"] = "Partition {0} does not exists on machine {1}".format(partition,hostname)
+            result["error"] = "Partition {0} does not exists on machine {1}".format(
+                partition, hostname)
             return result
 
     data["block_devices"] = disk_ids
@@ -583,13 +613,15 @@
     LOG.info(partition_ids)
     LOG.info(partitions)
 
-    #TODO validation
-    json_res = json.loads(maas.post(u"api/2.0/nodes/{0}/volume-groups/".format(system_id), None, **data).read())
+    # TODO validation
+    json_res = json.loads(maas.post(
+        u"api/2.0/nodes/{0}/volume-groups/".format(system_id), None, **data).read())
     LOG.info(json_res)
     result["new"] = "Volume group {0} created".format(json_res["name"])
 
     return result
 
+
 def delete_volume_group(hostname, name):
     '''
     Delete volume group on machine.
@@ -602,20 +634,21 @@
         salt-call maasng.delete_volume_group server_hostname vg0
     '''
 
-    maas=_create_maas_client()
+    maas = _create_maas_client()
     system_id = get_machine(hostname)["system_id"]
     LOG.info(system_id)
 
-    #TODO partitions
-    #for partition in partitions:
+    # TODO partitions
+    # for partition in partitions:
     #    temp = disk.split("-")
     #    disk_ids.append(str(_get_blockdevice_id_by_name(hostname, temp[] partition)))
 
-    #TODO partitions
+    # TODO partitions
     vg_id = name
 
-    #TODO validation
-    json_res = json.loads(maas.delete(u"api/2.0/nodes/{0}/volume-group/{1}/".format(system_id, vg_id)).read())
+    # TODO validation
+    json_res = json.loads(maas.delete(
+        u"api/2.0/nodes/{0}/volume-group/{1}/".format(system_id, vg_id)).read())
     LOG.info(json_res)
 
     return True
@@ -643,7 +676,7 @@
 
     data["size"] = bit_size
 
-    maas=_create_maas_client()
+    maas = _create_maas_client()
     system_id = get_machine(hostname)["system_id"]
     LOG.info(system_id)
 
@@ -651,57 +684,66 @@
 
     LOG.info(volume_group_id)
 
-    #TODO validation
-    json_res = json.loads(maas.post(u"api/2.0/nodes/{0}/volume-group/{1}/".format(system_id, volume_group_id), "create_logical_volume", **data).read())
+    # TODO validation
+    json_res = json.loads(maas.post(u"api/2.0/nodes/{0}/volume-group/{1}/".format(
+        system_id, volume_group_id), "create_logical_volume", **data).read())
     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
 
-def create_volume_filesystem(hostname, device, fs_type= None, mount = None):
 
-    maas=_create_maas_client()
+def create_volume_filesystem(hostname, device, fs_type=None, mount=None):
+
+    maas = _create_maas_client()
     system_id = get_machine(hostname)["system_id"]
 
     blockdevices_id = _get_blockdevice_id_by_name(hostname, device)
     data = {}
     if fs_type != None:
         data["fstype"] = fs_type
-        #TODO validation
-        json_res = json.loads(maas.post(u"/api/2.0/nodes/{0}/blockdevices/{1}/".format(system_id, blockdevices_id), "format", **data).read())
+        # TODO validation
+        json_res = json.loads(maas.post(u"/api/2.0/nodes/{0}/blockdevices/{1}/".format(
+            system_id, blockdevices_id), "format", **data).read())
         LOG.info(json_res)
 
     if mount != None:
         data["mount_point"] = mount
-        #TODO validation
-        json_res = json.loads(maas.post(u"/api/2.0/nodes/{0}/blockdevices/{1}/".format(system_id, blockdevices_id), "mount", **data).read())
+        # TODO validation
+        json_res = json.loads(maas.post(u"/api/2.0/nodes/{0}/blockdevices/{1}/".format(
+            system_id, blockdevices_id), "mount", **data).read())
         LOG.info(json_res)
 
     return True
 
-def create_partition_filesystem(hostname, partition, fs_type= None, mount = None):
 
-    maas=_create_maas_client()
+def create_partition_filesystem(hostname, partition, fs_type=None, mount=None):
+
+    maas = _create_maas_client()
     system_id = get_machine(hostname)["system_id"]
 
     blockdevices_id = _get_blockdevice_id_by_name(hostname, device)
     data = {}
     if fs_type != None:
         data["fstype"] = fs_type
-        #TODO validation
-        json_res = json.loads(maas.post(u"/api/2.0/nodes/{0}/blockdevices/{1}/".format(system_id, blockdevices_id), "format", **data).read())
+        # TODO validation
+        json_res = json.loads(maas.post(u"/api/2.0/nodes/{0}/blockdevices/{1}/".format(
+            system_id, blockdevices_id), "format", **data).read())
         LOG.info(json_res)
 
     if mount != None:
         data["mount_point"] = mount
-        #TODO validation
-        json_res = json.loads(maas.post(u"/api/2.0/nodes/{0}/blockdevices/{1}/".format(system_id, blockdevices_id), "mount", **data).read())
+        # TODO validation
+        json_res = json.loads(maas.post(u"/api/2.0/nodes/{0}/blockdevices/{1}/".format(
+            system_id, blockdevices_id), "mount", **data).read())
         LOG.info(json_res)
 
     return True
 
+
 def set_boot_disk(hostname, name):
     '''
     Create volume on volume group.
@@ -715,12 +757,142 @@
     '''
     data = {}
     result = {}
-    maas=_create_maas_client()
+    maas = _create_maas_client()
     system_id = get_machine(hostname)["system_id"]
     blockdevices_id = _get_blockdevice_id_by_name(hostname, name)
 
-    maas.post(u"/api/2.0/nodes/{0}/blockdevices/{1}/".format(system_id, blockdevices_id), "set_boot_disk", **data).read()
-    #TODO validation for error response (disk does not exists and node does not exists)
+    maas.post(u"/api/2.0/nodes/{0}/blockdevices/{1}/".format(
+        system_id, blockdevices_id), "set_boot_disk", **data).read()
+    # TODO validation for error response (disk does not exists and node does not exists)
     result["new"] = "Disk {0} was set as bootable".format(name)
 
     return result
+
+
+def list_fabric():
+    '''
+    Get list of all fabric
+
+    CLI Example:
+
+    .. code-block:: bash
+
+        salt 'maas-node' maasng.list_fabric
+    '''
+    fabrics = {}
+    maas = _create_maas_client()
+    json_res = json.loads(maas.get(u'api/2.0/fabrics/').read())
+    LOG.info(json_res)
+    for item in json_res:
+        fabrics[item["name"]] = item
+    return fabrics
+
+
+def create_fabric(name):
+    '''
+    Create new fabric.
+
+    CLI Example:
+
+    .. code-block:: bash
+
+        salt 'maas-node' maasng.create_fabric
+    '''
+    result = {}
+    data = {
+        "name": name,
+        "description": '',
+        "class_type": '',
+
+    }
+
+    maas = _create_maas_client()
+    json_res = json.loads(maas.post(u"api/2.0/fabrics/", None, **data).read())
+    LOG.info(json_res)
+    result["new"] = "Fabrics {0} created".format(json_res["name"])
+    return result
+
+
+def list_subnet():
+    '''
+    Get list of all subnets
+
+    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())
+    LOG.info(json_res)
+    for item in json_res:
+        subnets[item["name"]] = item
+    return subnets
+
+
+def list_vlans(fabric):
+    '''
+    Get list of all vlans for specific fabric
+
+    CLI Example:
+
+    .. code-block:: bash
+
+        salt 'maas-node' maasng.list_vlans
+    '''
+    vlans = {}
+    maas = _create_maas_client()
+    fabric_id = get_fabric(fabric)
+
+    json_res = json.loads(
+        maas.get(u'api/2.0/fabrics/{0}/vlans/'.format(fabric_id)).read())
+    LOG.info(json_res)
+    for item in json_res:
+        vlans[item["name"]] = item
+    return vlans
+
+
+def get_fabric(fabric):
+    '''
+    Get id for specific fabric
+
+    CLI Example:
+
+    .. code-block:: bash
+
+        salt-call maasng.get_fabric fabric_name
+    '''
+    try:
+        return list_fabric()[fabric]['id']
+    except KeyError:
+        return {"error": "Frabic not found on MaaS server"}
+
+
+def update_vlan(name, fabric, vid, description, dhcp_on=False):
+    '''
+    Update vlan
+
+    CLI Example:
+
+    .. code-block:: bash
+
+        salt 'maas-node' maasng.update_vlan name, fabric, vid, description, dhcp_on
+    '''
+    result = {}
+
+    data = {
+        "name": name,
+        "dhcp_on": str(dhcp_on),
+        "description": description,
+    }
+    maas = _create_maas_client()
+    fabric_id = get_fabric(fabric)
+
+    json_res = json.loads(maas.put(
+        u'api/2.0/fabrics/{0}/vlans/{1}/'.format(fabric_id, vid), **data).read())
+    print(json_res)
+    result["new"] = "Vlan {0} was updated".format(json_res["name"])
+
+    return result
diff --git a/_states/maasng.py b/_states/maasng.py
index 3210e5d..ad30025 100644
--- a/_states/maasng.py
+++ b/_states/maasng.py
@@ -17,13 +17,15 @@
     10: "raid-10",
 }
 
+
 def __virtual__():
     '''
     Load MaaSng module
     '''
     return 'maasng'
 
-def disk_layout_present(hostname, layout_type, root_size=None, root_device=None, volume_group=None, volume_name=None, volume_size=None, disk={} , **kwargs):
+
+def disk_layout_present(hostname, layout_type, root_size=None, root_device=None, volume_group=None, volume_name=None, volume_size=None, disk={}, **kwargs):
     '''
     Ensure that the disk layout does exist
 
@@ -36,7 +38,8 @@
 
     machine = __salt__['maasng.get_machine'](hostname)
     if "error" in machine:
-        ret['comment'] = "State execution failed for machine {0}".format(hostname)
+        ret['comment'] = "State execution failed for machine {0}".format(
+            hostname)
         ret['result'] = False
         ret['changes'] = machine
         return ret
@@ -47,16 +50,19 @@
 
     if __opts__['test']:
         ret['result'] = None
-        ret['comment'] = 'Disk layout will be updated on {0}, this action will delete current layout.'.format(hostname)
+        ret['comment'] = 'Disk layout will be updated on {0}, this action will delete current layout.'.format(
+            hostname)
         return ret
 
     if layout_type == "flat":
 
-        ret["changes"] = __salt__['maasng.update_disk_layout'](hostname, layout_type, root_size, root_device)
+        ret["changes"] = __salt__['maasng.update_disk_layout'](
+            hostname, layout_type, root_size, root_device)
 
     elif layout_type == "lvm":
 
-        ret["changes"] = __salt__['maasng.update_disk_layout'](hostname, layout_type, root_size, root_device, volume_group, volume_name, volume_size)
+        ret["changes"] = __salt__['maasng.update_disk_layout'](
+            hostname, layout_type, root_size, root_device, volume_group, volume_name, volume_size)
 
     else:
         ret["comment"] = "Not supported layout provided. Choose flat or lvm"
@@ -64,6 +70,7 @@
 
     return ret
 
+
 def raid_present(hostname, name, level, devices=[], partitions=[], partition_schema={}):
     '''
     Ensure that the raid does exist
@@ -78,7 +85,8 @@
 
     machine = __salt__['maasng.get_machine'](hostname)
     if "error" in machine:
-        ret['comment'] = "State execution failed for machine {0}".format(hostname)
+        ret['comment'] = "State execution failed for machine {0}".format(
+            hostname)
         ret['result'] = False
         ret['changes'] = machine
         return ret
@@ -89,18 +97,21 @@
 
     if __opts__['test']:
         ret['result'] = None
-        ret['comment'] = 'Raid {0} will be updated on {1}'.format(name,hostname)
+        ret['comment'] = 'Raid {0} will be updated on {1}'.format(
+            name, hostname)
         return ret
 
-    #Validate that raid exists
-    ##With correct devices/partition
-    #OR
-    ##Create raid
+    # Validate that raid exists
+    # With correct devices/partition
+    # OR
+    # Create raid
 
-    ret["changes"] = __salt__['maasng.create_raid'](hostname=hostname, name=name, level=level, disks=devices, partitions=partitions)
+    ret["changes"] = __salt__['maasng.create_raid'](
+        hostname=hostname, name=name, level=level, disks=devices, partitions=partitions)
 
-    #TODO partitions
-    ret["changes"].update(disk_partition_present(hostname, name, partition_schema)["changes"])
+    # TODO partitions
+    ret["changes"].update(disk_partition_present(
+        hostname, name, partition_schema)["changes"])
 
     if "error" in ret["changes"]:
         ret["result"] = False
@@ -115,15 +126,15 @@
     :param name: The name of the cloud that should not exist
     '''
 
-    #1. Validate that disk has correct values for size and mount
-        #a. validate count of partitions
-        #b. validate size of partitions
-    #2. If not delete all partitions on disk and recreate schema
-    #3. Validate type exists
-        #if should not exits
-        #delete mount and unformat
-    #4. Validate mount exists
-    #5. if not enforce umount or mount
+    # 1. Validate that disk has correct values for size and mount
+    # a. validate count of partitions
+    # b. validate size of partitions
+    # 2. If not delete all partitions on disk and recreate schema
+    # 3. Validate type exists
+    # if should not exits
+    # delete mount and unformat
+    # 4. Validate mount exists
+    # 5. if not enforce umount or mount
 
     ret = {'name': hostname,
            'changes': {},
@@ -132,7 +143,8 @@
 
     machine = __salt__['maasng.get_machine'](hostname)
     if "error" in machine:
-        ret['comment'] = "State execution failed for machine {0}".format(hostname)
+        ret['comment'] = "State execution failed for machine {0}".format(
+            hostname)
         ret['result'] = False
         ret['changes'] = machine
         return ret
@@ -148,7 +160,7 @@
 
     partitions = __salt__['maasng.list_partitions'](hostname, disk)
 
-    ##Calculate actual size in bytes from provided data
+    # Calculate actual size in bytes from provided data
     for part_name, part in partition_schema.iteritems():
         size, unit = part["size"][:-1], part["size"][-1]
         part["calc_size"] = int(size) * SIZE[unit]
@@ -157,12 +169,13 @@
 
         for part_name, part in partition_schema.iteritems():
             LOG.info('validated {0}'.format(part["calc_size"]))
-            LOG.info('validated {0}'.format(int(partitions[disk+"-"+part_name.split("-")[-1]]["size"])))
+            LOG.info('validated {0}'.format(
+                int(partitions[disk+"-"+part_name.split("-")[-1]]["size"])))
             if part["calc_size"] == int(partitions[disk+"-"+part_name.split("-")[-1]]["size"]):
                 LOG.info('validated')
-                #TODO validate size (size from maas is not same as calculate?)
-                #TODO validate mount
-                #TODO validate fs type
+                # TODO validate size (size from maas is not same as calculate?)
+                # TODO validate mount
+                # TODO validate fs type
             else:
                 LOG.info('breaking')
                 break
@@ -172,8 +185,9 @@
     LOG.info('delete')
     for partition_name, partition in partitions.iteritems():
         LOG.info(partition)
-        ##TODO IF LVM create ERROR
-        ret["changes"] = __salt__['maasng.delete_partition_by_id'](hostname, disk, partition["id"])
+        # TODO IF LVM create ERROR
+        ret["changes"] = __salt__['maasng.delete_partition_by_id'](
+            hostname, disk, partition["id"])
 
     LOG.info('recreating')
     for part_name, part in partition_schema.iteritems():
@@ -183,13 +197,15 @@
             part["mount"] = None
         if "type" not in part:
             part["type"] = None
-        ret["changes"] = __salt__['maasng.create_partition'](hostname, disk, part["size"], part["type"], part["mount"])
+        ret["changes"] = __salt__['maasng.create_partition'](
+            hostname, disk, part["size"], part["type"], part["mount"])
 
     if "error" in ret["changes"]:
         ret["result"] = False
 
     return ret
 
+
 def volume_group_present(hostname, name, devices=[], partitions=[]):
     '''
     Ensure that the disk layout does exist
@@ -197,13 +213,14 @@
     :param name: The name of the cloud that should not exist
     '''
     ret = {'name': hostname,
-        'changes': {},
-        'result': True,
-        'comment': 'LVM group {0} presented on {1}'.format(name,hostname)}
+           'changes': {},
+           'result': True,
+           'comment': 'LVM group {0} presented on {1}'.format(name, hostname)}
 
     machine = __salt__['maasng.get_machine'](hostname)
     if "error" in machine:
-        ret['comment'] = "State execution failed for machine {0}".format(hostname)
+        ret['comment'] = "State execution failed for machine {0}".format(
+            hostname)
         ret['result'] = False
         ret['changes'] = machine
         return ret
@@ -212,25 +229,28 @@
         ret['comment'] = 'Machine {0} is not in Ready state.'.format(hostname)
         return ret
 
-    #TODO validation if exists
+    # TODO validation if exists
     vgs = __salt__['maasng.list_volume_groups'](hostname)
 
     if name in vgs:
-        #TODO validation for devices and partitions
+        # TODO validation for devices and partitions
         return ret
 
     if __opts__['test']:
         ret['result'] = None
-        ret['comment'] = 'LVM group {0} will be updated on {1}'.format(name,hostname)
+        ret['comment'] = 'LVM group {0} will be updated on {1}'.format(
+            name, hostname)
         return ret
 
-    ret["changes"] = __salt__['maasng.create_volume_group'](hostname, name, devices, partitions)
+    ret["changes"] = __salt__['maasng.create_volume_group'](
+        hostname, name, devices, partitions)
 
     if "error" in ret["changes"]:
         ret["result"] = False
 
     return ret
 
+
 def volume_present(hostname, name, volume_group_name, size, type=None, mount=None):
     '''
     Ensure that the disk layout does exist
@@ -241,11 +261,12 @@
     ret = {'name': hostname,
            'changes': {},
            'result': True,
-           'comment': 'LVM group {0} presented on {1}'.format(name,hostname)}
+           'comment': 'LVM group {0} presented on {1}'.format(name, hostname)}
 
     machine = __salt__['maasng.get_machine'](hostname)
     if "error" in machine:
-        ret['comment'] = "State execution failed for machine {0}".format(hostname)
+        ret['comment'] = "State execution failed for machine {0}".format(
+            hostname)
         ret['result'] = False
         ret['changes'] = machine
         return ret
@@ -256,11 +277,13 @@
 
     if __opts__['test']:
         ret['result'] = None
-        ret['comment'] = 'LVM volume {0} will be updated on {1}'.format(name,hostname)
+        ret['comment'] = 'LVM volume {0} will be updated on {1}'.format(
+            name, hostname)
 
-    #TODO validation if exists
+    # TODO validation if exists
 
-    ret["changes"] = __salt__['maasng.create_volume'](hostname, name, volume_group_name, size, type, mount)
+    ret["changes"] = __salt__['maasng.create_volume'](
+        hostname, name, volume_group_name, size, type, mount)
 
     return ret
 
@@ -276,11 +299,12 @@
     ret = {'name': hostname,
            'changes': {},
            'result': True,
-           'comment': 'LVM group {0} presented on {1}'.format(name,hostname)}
+           'comment': 'LVM group {0} presented on {1}'.format(name, hostname)}
 
     machine = __salt__['maasng.get_machine'](hostname)
     if "error" in machine:
-        ret['comment'] = "State execution failed for machine {0}".format(hostname)
+        ret['comment'] = "State execution failed for machine {0}".format(
+            hostname)
         ret['result'] = False
         ret['changes'] = machine
         return ret
@@ -291,10 +315,44 @@
 
     if __opts__['test']:
         ret['result'] = None
-        ret['comment'] = 'LVM volume {0} will be updated on {1}'.format(name,hostname)
+        ret['comment'] = 'LVM volume {0} will be updated on {1}'.format(
+            name, hostname)
 
-    #TODO disk validation if exists
+    # TODO disk validation if exists
 
     ret["changes"] = __salt__['maasng.set_boot_disk'](hostname, name)
 
-    return ret
\ No newline at end of file
+    return ret
+
+
+def update_vlan(name, fabric, vid, description, dhcp_on=False):
+    '''
+
+    :param name: Name of vlan
+    :param fabric: Name of fabric
+    :param vid: Vlan id
+    :param description: Description of vlan
+    :param dhcp_on: State of dhcp
+
+    '''
+
+    ret = {'name': fabric,
+           'changes': {},
+           'result': True,
+           'comment': 'Module function maasng.update_vlan executed'}
+
+    ret["changes"] = __salt__['maasng.update_vlan'](
+        name=name, fabric=fabric, vid=vid, description=description, dhcp_on=dhcp_on)
+
+    if "error" in fabric:
+        ret['comment'] = "State execution failed for fabric {0}".format(fabric)
+        ret['result'] = False
+        ret['changes'] = fabric
+        return ret
+
+    if __opts__['test']:
+        ret['result'] = None
+        ret['comment'] = 'Vlan {0} will be updated for {1}'.format(vid, fabric)
+        return ret
+
+    return ret
diff --git a/maas/files/curtin_userdata_arm64_generic_xenial b/maas/files/curtin_userdata_arm64_generic_xenial
new file mode 100644
index 0000000..b070aaa
--- /dev/null
+++ b/maas/files/curtin_userdata_arm64_generic_xenial
@@ -0,0 +1,44 @@
+{%- from "maas/map.jinja" import cluster with context %}
+{% raw %}
+#cloud-config
+debconf_selections:
+ maas: |
+  {{for line in str(curtin_preseed).splitlines()}}
+  {{line}}
+  {{endfor}}
+{{if third_party_drivers and driver}}
+early_commands:
+  {{py: key_string = ''.join(['\\x%x' % x for x in map(ord, driver['key_binary'])])}}
+  driver_00_get_key: /bin/echo -en '{{key_string}}' > /tmp/maas-{{driver['package']}}.gpg
+  driver_01_add_key: ["apt-key", "add", "/tmp/maas-{{driver['package']}}.gpg"]
+  driver_02_add: ["add-apt-repository", "-y", "deb {{driver['repository']}} {{node.get_distro_series()}} main"]
+  driver_03_update_install: ["sh", "-c", "apt-get update --quiet && apt-get --assume-yes install {{driver['package']}}"]
+  driver_04_load: ["sh", "-c", "depmod && modprobe {{driver['module']}}"]
+{{endif}}
+late_commands:
+  maas: [wget, '--no-proxy', {{node_disable_pxe_url|escape.json}}, '--post-data', {{node_disable_pxe_data|escape.json}}, '-O', '/dev/null']
+{% endraw %}
+{%- if not cluster.saltstack_repo_key == 'none' %}
+{% set salt_repo_key = salt['hashutil.base64_b64encode'](cluster.saltstack_repo_key) %}
+  apt_00_set_gpg: ["curtin", "in-target", "--", "sh", "-c", "echo '{{salt_repo_key}}' | base64 -d | apt-key add -"]
+{%- endif %}
+{#- NOTE: Re-use amd64 repos on arm64 since most packages are arch independent -#}
+  apt_01_set_repo: ["curtin", "in-target", "--", "sh", "-c", "echo 'deb [arch=amd64] {{ cluster.saltstack_repo_xenial }}' >> /etc/apt/sources.list"]
+{% raw %}
+  apt_03_update: ["curtin", "in-target", "--", "apt-get", "update"]
+  salt_01_install: ["curtin", "in-target", "--", "apt-get", "-y", "install", "python-futures", "salt-minion"]
+{% endraw %}
+  salt_02_hostname_set: ["curtin", "in-target", "--", "echo", "{% raw %}{{node.hostname}}{% endraw %}.{{pillar.linux.system.domain}}"]
+  salt_03_hostname_get: ["curtin", "in-target", "--", "sh", "-c", "echo 'id: {% raw %}{{node.hostname}}{% endraw %}.{{pillar.linux.system.domain}}' >> /etc/salt/minion"]
+  salt_04_master: ["curtin", "in-target", "--", "sh", "-c", "echo 'master: {{ salt_master_ip }}' >> /etc/salt/minion"]
+{% raw %}
+{{if third_party_drivers and driver}}
+  driver_00_key_get: curtin in-target -- sh -c "/bin/echo -en '{{key_string}}' > /tmp/maas-{{driver['package']}}.gpg"
+  driver_02_key_add: ["curtin", "in-target", "--", "apt-key", "add", "/tmp/maas-{{driver['package']}}.gpg"]
+  driver_03_add: ["curtin", "in-target", "--", "add-apt-repository", "-y", "deb {{driver['repository']}} {{node.get_distro_series()}} main"]
+  driver_04_update_install: ["curtin", "in-target", "--", "apt-get", "update", "--quiet"]
+  driver_05_install: ["curtin", "in-target", "--", "apt-get", "-y", "install", "{{driver['package']}}"]
+  driver_06_depmod: ["curtin", "in-target", "--", "depmod"]
+  driver_07_update_initramfs: ["curtin", "in-target", "--", "update-initramfs", "-u"]
+{{endif}}
+{% endraw %}
diff --git a/maas/region.sls b/maas/region.sls
index 47ac9d7..e129457 100644
--- a/maas/region.sls
+++ b/maas/region.sls
@@ -97,6 +97,18 @@
   - require:
     - pkg: maas_region_packages
 
+/etc/maas/preseeds/curtin_userdata_arm64_generic_xenial:
+  file.managed:
+  - source: salt://maas/files/curtin_userdata_arm64_generic_xenial
+  - template: jinja
+  - user: root
+  - group: root
+  - mode: 644
+  - context:
+      salt_master_ip: {{ region.salt_master_ip }}
+  - require:
+    - pkg: maas_region_packages
+
 /root/.pgpass:
   file.managed:
   - source: salt://maas/files/pgpass
@@ -232,6 +244,21 @@
   - require:
     - module: maas_config
 
+{%- if region.fabrics is defined %}
+{%- for fabric_name, fabric in region.fabrics.iteritems() %}
+{%- for vid, vlan in fabric.get('vlan',{}).items() %}
+maas_update_vlan_for_{{ fabric_name }}_{{ vid }}:
+  maasng.update_vlan:
+  - vid: {{ vid }}
+  - fabric: {{ fabric_name }}
+  - name: {{ vlan.get('name','') }}
+  - description: {{ vlan.description }}
+  - dhcp_on: {{ vlan.get('dhcp','False') }}
+{%- endfor %}
+{%- endfor %}
+{%- endif %}
+
+
 {%- if region.get('sshprefs', False)  %}
 maas_sshprefs:
   module.run: