Implement boot-resources control

  * Allow to manage boot-resources repo
  * Allow to romove 'undefined' boot-resources repos
  * Allow to select boot-res. selections. Including for unmanaged bs repos
  * Misc: fix reuirment for non-related func
  * Fix dep's for maas_config
  * Disable rsyslog test pillar for kitchen
    - Salt always fail tests with:
      'Comment: Service rsyslog is already enabled, and is dead'

Partial-Bug: PROD-16412 (PROD:PROD-16412)

Change-Id: Idb86ffd35ef7e9fe6ce99ca5bcdd87570f8a70c4
diff --git a/_modules/maasng.py b/_modules/maasng.py
index 8e4d1f2..16fe192 100644
--- a/_modules/maasng.py
+++ b/_modules/maasng.py
@@ -982,9 +982,266 @@
 
     json_res = json.loads(maas.put(
         u'api/2.0/fabrics/{0}/vlans/{1}/'.format(fabric_id, vid), **data).read())
-    print(json_res)
+    LOG.debug("update_vlan:{}".format(json_res))
     result["new"] = "Vlan {0} was updated".format(json_res["name"])
 
     return result
 
 # END NETWORKING
+
+# MAAS CONFIG SECTION
+
+
+def _get_boot_source_id_by_url(url):
+    # FIXME: fix ret\validation
+    try:
+        bs_id = get_boot_source(url=url)["id"]
+    except KeyError:
+        return {"error": "boot-source:{0} not exist!".format(url)}
+    return bs_id
+
+
+def get_boot_source(url=None):
+    """
+    Read a boot source by url. If url not specified - return all.
+
+    CLI Example:
+
+    .. code-block:: bash
+
+        salt 'maas-node' maasng.get_boot_source url
+
+    """
+    boot_sources = {}
+    maas = _create_maas_client()
+    json_res = json.loads(maas.get(u'api/2.0/boot-sources/').read() or 'null')
+    for item in json_res:
+        boot_sources[str(item["url"])] = item
+    if url:
+        return boot_sources.get(url, {})
+    return boot_sources
+
+
+def delete_boot_source(url, bs_id=None):
+    """
+    Delete a boot source by url.
+
+    CLI Example:
+
+    .. code-block:: bash
+
+        sal 'maas-node' maasng.delete url
+
+    """
+    result = {}
+    if not bs_id:
+        bs_id = _get_boot_source_id_by_url(url)
+    maas = _create_maas_client()
+    json_res = json.loads(maas.delete(
+        u'/api/2.0/boot-sources/{0}/'.format(bs_id)).read() or 'null')
+    LOG.debug("delete_boot_source:{}".format(json_res))
+    result["new"] = "Boot-resource {0} deleted".format(url)
+    return result
+
+
+def boot_sources_delete_all_others(except_urls=[]):
+    """
+    Delete all boot-sources, except defined in 'except_urls' list.
+    """
+    result = {}
+    maas_boot_sources = get_boot_source()
+    if 0 in [len(except_urls), len(maas_boot_sources)]:
+        result['result'] = None
+        result[
+            "comment"] = "Exclude or maas sources for delete empty. No changes goinng to be."
+        return result
+    for url in maas_boot_sources.keys():
+        if url not in except_urls:
+            LOG.info("Removing boot-source:{}".format(url))
+            boot_resources_import(action='stop_import', wait=True)
+            result["changes"] = delete_boot_source(url)
+    return result
+
+
+def create_boot_source(url, keyring_filename='', keyring_data='', wait=False):
+    """
+    Create and import maas boot-source: link to maas-ephemeral repo
+    Be aware, those step will import resource to rack ctrl, but you also need to import
+    them into the region!
+
+
+    :param url:               The URL of the BootSource.
+    :param keyring_filename:  The path to the keyring file for this BootSource.
+    :param keyring_data:      The GPG keyring for this BootSource, base64-encoded data.
+
+    """
+
+    # TODO: not work with 'update' currently => keyring update may fail.
+    result = {}
+
+    data = {
+        "url": url,
+        "keyring_filename": keyring_filename,
+        "keyring_data": str(keyring_data),
+    }
+
+    maas = _create_maas_client()
+    ipdb.set_trace()
+    if url in get_boot_source():
+        result['result'] = None
+        result["comment"] = "boot resource already exist"
+        return result
+
+    # NOTE: maas.post will return 400, if url already defined.
+    json_res = json.loads(
+        maas.post(u'api/2.0/boot-sources/', None, **data).read())
+    if wait:
+        LOG.debug(
+            "Sleep for 5s,to get MaaS some time to process previous request")
+        time.sleep(5)
+        ret = boot_resources_is_importing(wait=True)
+        if ret is dict:
+            return ret
+    LOG.debug("create_boot_source:{}".format(json_res))
+    result["new"] = "boot resource {0} was created".format(json_res["url"])
+
+    return result
+
+
+def boot_resources_import(action='import', wait=False):
+    """
+    import/stop_import the boot resources.
+
+    :param action:  import\stop_import
+    :param wait:    True\False. Wait till process finished.
+
+    CLI Example:
+
+    .. code-block:: bash
+
+        salt 'maas-node' maasng.boot_resources_import action='import'
+
+    """
+    maas = _create_maas_client()
+    # Have no idea why, but usual jsonloads not work here..
+    imp = maas.post(u'api/2.0/boot-resources/', action)
+    if imp.code == 200:
+        LOG.debug('boot_resources_import:{}'.format(imp.readline()))
+        if wait:
+            boot_resources_is_importing(wait=True)
+        return True
+    else:
+        return False
+
+
+def boot_resources_is_importing(wait=False):
+    maas = _create_maas_client()
+    result = {}
+    if wait:
+        started_at = time.time()
+        poll_time = 5
+        timeout = 60 * 15
+        while boot_resources_is_importing(wait=False):
+            c_timeout = timeout - (time.time() - started_at)
+            if c_timeout <= 0:
+                result['result'] = False
+                result["comment"] = "Boot-resources import not finished in time"
+                return result
+            LOG.info(
+                "Waiting boot-resources import done\n"
+                "sleep for:{}s "
+                "Left:{}/{}s".format(poll_time, round(c_timeout), timeout))
+            time.sleep(poll_time)
+        return json.loads(
+            maas.get(u'api/2.0/boot-resources/', 'is_importing').read())
+    else:
+        return json.loads(
+            maas.get(u'api/2.0/boot-resources/', 'is_importing').read())
+
+#####
+#def boot_sources_selections_delete_all_others(except_urls=[]):
+#    """
+#    """
+#    result = {}
+#    return result
+
+
+def is_boot_source_selections_in(dict1, list1):
+    """
+    Check that requested boot-selection already in maas bs selections, if True- return bss id.
+    # FIXME: those hack check doesn't look good.
+    """
+    for bs in list1:
+        same = set(dict1.keys()) & set(bs.keys())
+        if all(elem in same for elem in
+               ['os', 'release', 'arches', 'subarches', 'labels']):
+            LOG.debug(
+                "boot-selection in maas:{0}\nlooks same to requested:{1}".format(
+                    bs, dict1))
+            return bs['id']
+    return False
+
+
+def get_boot_source_selections(bs_url):
+    """
+    Get boot-source selections.
+    """
+    # check for key_error!
+    bs_id = _get_boot_source_id_by_url(bs_url)
+    maas = _create_maas_client()
+    json_res = json.loads(
+        maas.get(u'/api/2.0/boot-sources/{0}/selections/'.format(bs_id)).read())
+    LOG.debug(
+        "get_boot_source_selections for url:{} \n{}".format(bs_url, json_res))
+    return json_res
+
+
+def create_boot_source_selections(bs_url, os, release, arches="*",
+                                  subarches="*", labels="*", wait=True):
+    """
+         Create a new boot source selection for bs_url.
+        :param os:        The OS (e.g. ubuntu, centos) for which to import resources.Required.
+        :param release:   The release for which to import resources. Required.
+        :param arches:    The architecture list for which to import resources.
+        :param subarches: The subarchitecture list for which to import resources.
+        :param labels:    The label lists for which to import resources.
+    """
+
+    result = {}
+
+    data = {
+        "os": os,
+        "release": release,
+        "arches": arches,
+        "subarches": subarches,
+        "labels": labels,
+    }
+
+    maas = _create_maas_client()
+    bs_id = _get_boot_source_id_by_url(bs_url)
+    # TODO add pre-create verify
+    maas_bs_s = get_boot_source_selections(bs_url)
+    if is_boot_source_selections_in(data, maas_bs_s):
+        result["result"] = True
+        result[
+            "comment"] = 'Requested boot-source selection for {0} already exist.'.format(
+            bs_url)
+        return result
+
+    # NOTE: maas.post will return 400, if url already defined.
+    json_res = json.loads(
+        maas.post(u'api/2.0/boot-sources/{0}/selections/'.format(bs_id), None,
+                  **data).read())
+    LOG.debug("create_boot_source_selections:{}".format(json_res))
+    if wait:
+        LOG.debug(
+            "Sleep for 5s,to get MaaS some time to process previous request")
+        time.sleep(5)
+        ret = boot_resources_import(action='import', wait=True)
+        if ret is dict:
+            return ret
+    result["new"] = "boot-source selection for {0} was created".format(bs_url)
+
+    return result
+
+# END MAAS CONFIG SECTION