Add custom layout partitioning

  * In case of 'non-clean' partitioning, we may stuck in 400 error from MAAS
    The  RC of such issue, that we trying to apply totally custom schema, to
    any auto-predefined(or not cleaned previosly)
  * Add new layout: custom - which means, drop everything befere start part.
  * Misc: remove broken and unused func `create_partition_filesystem`

Closes-Bug: PROD-20317 (PROD:20317) 

Change-Id: I574c669616b9318b8ecafaf9c8ad4162c01b44e1
diff --git a/_modules/maasng.py b/_modules/maasng.py
index 65ffc84..8e4d1f2 100644
--- a/_modules/maasng.py
+++ b/_modules/maasng.py
@@ -1,5 +1,5 @@
 # -*- coding: utf-8 -*-
-'''
+"""
 Module for handling maas calls.
 
 :optdepends:    pyapi-maas Python adapter
@@ -9,7 +9,7 @@
         maas.url: 'https://maas.domain.com/'
         maas.token: fdsfdsdsdsfa:fsdfae3fassd:fdsfdsfsafasdfsa
 
-'''
+"""
 
 from __future__ import absolute_import
 
@@ -50,10 +50,10 @@
 
 
 def __virtual__():
-    '''
+    """
     Only load this module if maas-client
     is installed on this minion.
-    '''
+    """
     if HAS_MASS:
         return 'maasng'
     return False
@@ -95,6 +95,15 @@
     return list_volume_groups(hostname)[device]["id"]
 
 
+def _get_volume_id_by_name(hostname, volume_name, volume_group, maas_volname=True):
+
+    if not maas_volname:
+        # MAAS-like name
+        volume_name = str("%s-%s" % (volume_group,volume_name))
+    ##TODO validation
+    return get_volumes(hostname, volume_group)[volume_name]["id"]
+
+
 def _get_partition_id_by_name(hostname, device, partition):
 
     # TODO validation
@@ -104,7 +113,7 @@
 
 
 def get_machine(hostname):
-    '''
+    """
     Get information aboout specified machine
 
     CLI Example:
@@ -112,7 +121,7 @@
     .. code-block:: bash
 
         salt-call maasng.get_machine server_hostname
-    '''
+    """
     try:
         return list_machines()[hostname]
     except KeyError:
@@ -120,7 +129,7 @@
 
 
 def list_machines():
-    '''
+    """
     Get list of all machines from maas server
 
     CLI Example:
@@ -128,7 +137,7 @@
     .. code-block:: bash
 
         salt 'maas-node' maasng.list_machines
-    '''
+    """
     machines = {}
     maas = _create_maas_client()
     json_res = json.loads(maas.get(u'api/2.0/machines/').read())
@@ -148,14 +157,12 @@
 
     return False
 
-# MACHINE OPERATIONS
-# TODO
-
+# END MACHINE SECTION
 # RAID SECTION
 
 
 def create_raid(hostname, name, level, disks=[], partitions=[], **kwargs):
-    '''
+    """
     Create new raid on machine.
 
     CLI Example:
@@ -163,7 +170,7 @@
     .. code-block:: bash
 
         salt-call maasng.create_raid hostname=kvm03 name=md0 level=1 disks=[vdb,vdc] partitions=[vdd-part1,vde-part1]
-    '''
+    """
 
     result = {}
 
@@ -213,7 +220,7 @@
 
 
 def list_raids(hostname):
-    '''
+    """
     Get list all raids on machine
 
     CLI Example:
@@ -221,22 +228,21 @@
     .. code-block:: bash
 
         salt-call maasng.list_raids server_hostname
-    '''
+    """
 
+    raids = {}
     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())
-    LOG.info(json_res)
-
-    # TODO return list of raid devices
-    return True
+    #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
+    return raids
 
 
 def get_raid(hostname, name):
-    '''
+    """
     Get information about specific raid on machine
 
     CLI Example:
@@ -244,15 +250,42 @@
     .. code-block:: bash
 
         salt-call maasng.get_raids server_hostname md0
-    '''
+    """
 
     return list_raids(hostname)[name]
 
 
+def _get_raid_id_by_name(hostname, raid_name):
+    return get_raid(hostname, raid_name)['id']
+
+
+def delete_raid(hostname, raid_name):
+    """
+    Delete RAID on a machine.
+
+    CLI Example:
+
+    .. code-block:: bash
+
+        salt 'maas-node' maasng.delete_raid server_hostname raid_name
+        salt-call maasng.delete_raid server_hostname raid_name
+    """
+    result = {}
+    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()
+
+    result["new"] = "Raid {0} deleted".format(raid_name)
+    return result
+
+# END RAID SECTION
 # BLOCKDEVICES SECTION
 
+
 def list_blockdevices(hostname):
-    '''
+    """
     Get list of all blockdevices (disks) on machine
 
     CLI Example:
@@ -261,7 +294,7 @@
 
         salt 'maas-node' maasng.list_blockdevices server_hostname
         salt-call maasng.list_blockdevices server_hostname
-    '''
+    """
     ret = {}
 
     maas = _create_maas_client()
@@ -280,7 +313,7 @@
 
 
 def get_blockdevice(hostname, name):
-    '''
+    """
     Get information about blockdevice (disk) on machine
 
     CLI Example:
@@ -289,15 +322,16 @@
 
         salt 'maas-node' maasng.get_blockdevice server_hostname sda
         salt-call maasng.get_blockdevice server_hostname sda
-    '''
+    """
 
     return list_blockdevices(hostname)[name]
 
-
+# END BLOCKDEVICES SECTION
 # PARTITIONS
 
+
 def list_partitions(hostname, device):
-    '''
+    """
     Get list of all partitions on specific device located on specific machine
 
     CLI Example:
@@ -306,7 +340,7 @@
 
         salt 'maas-node' maasng.list_partitions server_hostname sda
         salt-call maasng.list_partitions server_hostname sda
-    '''
+    """
     ret = {}
     maas = _create_maas_client()
     system_id = get_machine(hostname)["system_id"]
@@ -327,7 +361,7 @@
 
 
 def get_partition(hostname, device, partition):
-    '''
+    """
     Get information about specific parition on device located on machine
 
     CLI Example:
@@ -338,13 +372,13 @@
         salt-call maasng.get_partition server_hostname disk_name partition
 
         root_size = size in GB
-    '''
+    """
 
     return list_partitions(partition)[name]
 
 
 def create_partition(hostname, disk, size, fs_type=None, mount=None):
-    '''
+    """
     Create new partition on device.
 
     CLI Example:
@@ -353,7 +387,7 @@
 
         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
     result = {}
     maas = _create_maas_client()
@@ -404,7 +438,7 @@
 
 
 def delete_partition(hostname, disk, partition_name):
-    '''
+    """
     Delete partition on device.
 
     CLI Example:
@@ -415,7 +449,7 @@
         salt-call maasng.delete_partition server_hostname disk_name partition_name
 
         root_size = size in GB
-    '''
+    """
     result = {}
     data = {}
     maas = _create_maas_client()
@@ -434,7 +468,7 @@
 
 
 def delete_partition_by_id(hostname, disk, partition_id):
-    '''
+    """
     Delete partition on device. Partition spefified by id of parition
 
     CLI Example:
@@ -445,7 +479,7 @@
         salt-call maasng.delete_partition_by_id server_hostname disk_name partition_id
 
         root_size = size in GB
-    '''
+    """
     result = {}
     data = {}
     maas = _create_maas_client()
@@ -459,13 +493,41 @@
         system_id, device_id, partition_id)).read()
     result["new"] = "Partition {0} deleted".format(partition_id)
     return result
+# END PARTITIONS
+# DISK LAYOUT
 
-# CREATE DISK LAYOUT
-# TODO
+
+def drop_storage_schema(hostname,disk=None):
+    """
+    #1. Drop lv
+    #2. Drop vg
+    #3. Drop md # need to zero-block?
+    #3. Drop part
+    """
+
+    if __opts__['test']:
+        ret['result'] = None
+        ret['comment'] = 'Storage schema on {0} will be removed'.format(hostname)
+        return ret
+    #TODO validation if exists
+    vgs = list_volume_groups(hostname)
+    for vg in vgs:
+        delete_volume_group(hostname, vg)
+
+    raids = list_raids(hostname)
+    for raid in raids:
+        delete_raid(hostname, raid)
+
+    blocks = list_blockdevices(hostname)
+    for block_d in blocks:
+        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"])
 
 
 def update_disk_layout(hostname, layout, root_size=None, root_device=None, volume_group=None, volume_name=None, volume_size=None):
-    '''
+    """
     Update disk layout. Flat or LVM layout supported.
 
     CLI Example:
@@ -476,7 +538,7 @@
         salt-call maasng.update_disk_layout server_hostname lvm root_size=None, root_device=None, volume_group=None, volume_name=None, volume_size=None
 
         root_size = size in GB
-    '''
+    """
     result = {}
     data = {
         "storage_layout": layout,
@@ -486,6 +548,14 @@
     system_id = get_machine(hostname)["system_id"]
     LOG.info(system_id)
 
+    if layout == 'custom':
+        drop_storage_schema(hostname)
+        result["new"] = {
+            "storage_layout": layout,
+        }
+
+        return result
+
     if root_size != None:
         bit_size = str(root_size * 1073741824)
         LOG.info(bit_size)
@@ -518,12 +588,11 @@
 
     return result
 
-
+# END DISK LAYOUT
 # LVM
-# TODO
 
 def list_volume_groups(hostname):
-    '''
+    """
     Get list of all volume group on machine.
 
     CLI Example:
@@ -532,7 +601,7 @@
 
         salt 'maas-node' maasng.list_volume_groups server_hostname
         salt-call maasng.list_volume_groups server_hostname
-    '''
+    """
     volume_groups = {}
 
     maas = _create_maas_client()
@@ -551,7 +620,7 @@
 
 
 def get_volume_group(hostname, name):
-    '''
+    """
     Get information about specific volume group on machine.
 
     CLI Example:
@@ -560,13 +629,13 @@
 
         salt 'maas-node' maasng.list_blockdevices server_hostname
         salt-call maasng.list_blockdevices server_hostname
-    '''
+    """
     # TODO validation that exists
     return list_volume_groups(hostname)[name]
 
 
 def create_volume_group(hostname, volume_group_name, disks=[], partitions=[]):
-    '''
+    """
     Create new volume group on machine. Disks or partitions needs to be provided.
 
     CLI Example:
@@ -575,7 +644,7 @@
 
         salt 'maas-node' maasng.create_volume_group volume_group_name, disks=[sda,sdb], partitions=[]
         salt-call maasng.create_volume_group server_hostname
-    '''
+    """
     result = {}
 
     data = {
@@ -623,7 +692,7 @@
 
 
 def delete_volume_group(hostname, name):
-    '''
+    """
     Delete volume group on machine.
 
     CLI Example:
@@ -632,30 +701,25 @@
 
         salt 'maas-node' maasng.delete_volume_group server_hostname vg0
         salt-call maasng.delete_volume_group server_hostname vg0
-    '''
+    """
 
     maas = _create_maas_client()
     system_id = get_machine(hostname)["system_id"]
-    LOG.info(system_id)
+    LOG.debug('delete_volume_group:{}'.format(system_id))
 
-    # TODO partitions
-    # for partition in partitions:
-    #    temp = disk.split("-")
-    #    disk_ids.append(str(_get_blockdevice_id_by_name(hostname, temp[] partition)))
+    vg_id = str(_get_volume_group_id_by_name(hostname, name))
+    for vol in get_volumes(hostname, name):
+        delete_volume(hostname,vol,name)
 
-    # 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() or 'null')
     LOG.info(json_res)
 
     return True
 
 
 def create_volume(hostname, volume_name, volume_group, size, fs_type=None, mount=None):
-    '''
+    """
     Create volume on volume group.
 
     CLI Example:
@@ -664,7 +728,7 @@
 
         salt 'maas-node' maasng.create_volume server_hostname volume_name, volume_group, size, fs_type=None, mount=None
         salt-call maasng.create_volume server_hostname volume_name, volume_group, size, fs_type=None, mount=None
-    '''
+    """
 
     data = {
         "name": volume_name,
@@ -690,12 +754,59 @@
     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 delete_volume(hostname, volume_name, volume_group):
+    """
+    Delete volume from volume group.
+    Tips: maas always use 'volume_group-volume_name' name schema.Example: 'vg0-glusterfs'
+          This function expexts same format.
+
+    CLI Example:
+
+    .. code-block:: bash
+
+        salt 'maas-node' maasng.delete_volume server_hostname volume_name volume_group
+        salt 'maas-node' maasng.delete_volume server_hostname vg0-vol0 vg0
+        salt-call maasng.delete_volume server_hostname volume_name volume_group
+    """
+
+    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))
+
+    if None in [volume_group_id, volume_id]:
+        return False
+
+    data = {
+        "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')
+    return True
+
+
+def get_volumes(hostname, vg_name):
+    """
+    Get list of volumes in volume group.
+    """
+    volumes = {}
+    _volumes = list_volume_groups(hostname)[vg_name].get('logical_volumes', False)
+    if _volumes:
+        for item in _volumes:
+            volumes[item["name"]] = item
+    return volumes
+
+# END LVM
+
+
 def create_volume_filesystem(hostname, device, fs_type=None, mount=None):
 
     maas = _create_maas_client()
@@ -720,32 +831,8 @@
     return True
 
 
-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())
-        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())
-        LOG.info(json_res)
-
-    return True
-
-
 def set_boot_disk(hostname, name):
-    '''
+    """
     Create volume on volume group.
 
     CLI Example:
@@ -754,7 +841,7 @@
 
         salt 'maas-node' maasng.set_boot_disk server_hostname disk_name
         salt-call maasng.set_boot_disk server_hostname disk_name
-    '''
+    """
     data = {}
     result = {}
     maas = _create_maas_client()
@@ -768,9 +855,11 @@
 
     return result
 
+# NETWORKING
+
 
 def list_fabric():
-    '''
+    """
     Get list of all fabric
 
     CLI Example:
@@ -778,7 +867,7 @@
     .. 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())
@@ -789,7 +878,7 @@
 
 
 def create_fabric(name):
-    '''
+    """
     Create new fabric.
 
     CLI Example:
@@ -797,7 +886,7 @@
     .. code-block:: bash
 
         salt 'maas-node' maasng.create_fabric
-    '''
+    """
     result = {}
     data = {
         "name": name,
@@ -814,7 +903,7 @@
 
 
 def list_subnet():
-    '''
+    """
     Get list of all subnets
 
     CLI Example:
@@ -822,7 +911,7 @@
     .. 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())
@@ -833,7 +922,7 @@
 
 
 def list_vlans(fabric):
-    '''
+    """
     Get list of all vlans for specific fabric
 
     CLI Example:
@@ -841,7 +930,7 @@
     .. code-block:: bash
 
         salt 'maas-node' maasng.list_vlans
-    '''
+    """
     vlans = {}
     maas = _create_maas_client()
     fabric_id = get_fabric(fabric)
@@ -855,7 +944,7 @@
 
 
 def get_fabric(fabric):
-    '''
+    """
     Get id for specific fabric
 
     CLI Example:
@@ -863,7 +952,7 @@
     .. code-block:: bash
 
         salt-call maasng.get_fabric fabric_name
-    '''
+    """
     try:
         return list_fabric()[fabric]['id']
     except KeyError:
@@ -871,7 +960,7 @@
 
 
 def update_vlan(name, fabric, vid, description, primary_rack, dhcp_on=False):
-    '''
+    """
     Update vlan
 
     CLI Example:
@@ -879,7 +968,7 @@
     .. code-block:: bash
 
         salt 'maas-node' maasng.update_vlan name, fabric, vid, description, dhcp_on
-    '''
+    """
     result = {}
 
     data = {
@@ -897,3 +986,5 @@
     result["new"] = "Vlan {0} was updated".format(json_res["name"])
 
     return result
+
+# END NETWORKING