Merge "maasng: list_machines, delete_machines"
diff --git a/.travis.yml b/.travis.yml
index 4b3a46e..44f9b27 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -20,7 +20,8 @@
     gem 'test-kitchen'
     gem 'kitchen-docker'
     gem 'kitchen-inspec'
-    gem 'inspec'
+    gem 'inspec', '<3.0.0'
+    #Version was frozen, because of issues in the version of inspec >3.0.0 -- see https://mirantis.jira.com/browse/PROD-24324 for more info
     gem 'kitchen-salt'   #, :git => 'https://github.com/salt-formulas/kitchen-salt.git'
   - bundle install
 
diff --git a/_modules/__init__.py b/_modules/__init__.py
deleted file mode 100644
index e69de29..0000000
--- a/_modules/__init__.py
+++ /dev/null
diff --git a/_modules/maas.py b/_modules/maas.py
index 426aff5..b0f5bb2 100644
--- a/_modules/maas.py
+++ b/_modules/maas.py
@@ -421,37 +421,30 @@
             'mac_addresses': machine_pxe_mac,
             'power_type': power_data.get('power_type', 'manual'),
         }
-        if 'power_address' in power_data:
-            data['power_parameters_power_address'] = power_data['power_address']
-        if 'power_driver' in power_data:
-            data['power_parameters_power_driver'] = power_data['power_driver']
-        if 'power_user' in power_data:
-            data['power_parameters_power_user'] = power_data['power_user']
-        if 'power_password' in power_data:
-            data['power_parameters_power_pass'] = \
-                power_data['power_password']
-        if 'power_id' in power_data:
-            data['power_parameters_power_id'] = power_data['power_id']
-        if 'power_nova_id' in power_data:
-            data['power_parameters_nova_id'] = power_data['power_nova_id']
-        if 'power_os_tenantname' in power_data:
-            data['power_parameters_os_tenantname'] = power_data['power_os_tenantname']
-        if 'power_os_username' in power_data:
-            data['power_parameters_os_username'] = power_data['power_os_username']
-        if 'power_os_password' in power_data:
-            data['power_parameters_os_password'] = power_data['power_os_password']
-        if 'power_os_authurl' in power_data:
-            data['power_parameters_os_authurl'] = power_data['power_os_authurl']
+        for k,v in power_data.items():
+            if k == 'power_type':
+                continue
+            data_key = 'power_parameters_{}'.format(k)
+            data[data_key] = v
         return data
 
     def update(self, new, old):
+        LOG.debug('Updating machine')
         old_macs = set(v['mac_address'].lower() for v in old['interface_set'])
-        if new['mac_addresses'].lower() not in old_macs:
+        LOG.debug('old_macs: %s' % old_macs)
+        if isinstance(new['mac_addresses'], list):
+            new_macs = set(v.lower() for v in new['mac_addresses'])
+        else:
+            new_macs = set([new['mac_addresses'].lower()])
+        LOG.debug('new_macs: %s' % new_macs)
+        intersect = list(new_macs.intersection(old_macs))
+        if not intersect:
             self._update = False
             LOG.info('Mac changed deleting old machine %s', old['system_id'])
             self._maas.delete(u'api/2.0/machines/{0}/'
                               .format(old['system_id']))
         else:
+            new['mac_addresses'] = intersect
             new[self._update_key] = str(old[self._update_key])
         return new
 
diff --git a/_modules/maasng.py b/_modules/maasng.py
index 3a65267..90e04f1 100644
--- a/_modules/maasng.py
+++ b/_modules/maasng.py
@@ -1490,13 +1490,14 @@
     """
     Delete all boot-sources, except defined in 'except_urls' list.
     """
-    result = {}
+    result = {'changes': {}}
     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."
+        result["comment"] = "'except_urls' or 'maas boot-sources' for " \
+                            "delete empty. No changes goinng to be."
         return result
+    # FIXME: fix 'changes' accumulator
     for url in maas_boot_sources.keys():
         if url not in except_urls:
             LOG.info("Removing boot-source:{}".format(url))
@@ -1675,7 +1676,7 @@
     # at least simple retry ;(
     json_res = False
     poll_time = 5
-    for i in range(0, 5):
+    for i in range(0, 10):
         try:
             json_res = json.loads(
                 maas.post(u'api/2.0/boot-sources/{0}/selections/'.format(bs_id), None,
@@ -1684,8 +1685,8 @@
             m = inst.readlines()
             LOG.warning("boot_source_selections "
                         "catch error during processing. Most-probably, "
-                        "streams not imported yet.\nSleep:{}s"
-                        "Retry:{}/5".format(poll_time, i))
+                        "streams data not imported yet.\nSleep:{}s "
+                        "Retry:{}/10".format(poll_time, i))
             LOG.warning("Message:{0}".format(m))
             time.sleep(poll_time)
             continue
diff --git a/_modules/multipart.py b/_modules/multipart.py
index 7ed17a6..45aebf3 100644
--- a/_modules/multipart.py
+++ b/_modules/multipart.py
@@ -88,9 +88,12 @@
 
 def build_multipart_message(data):
     message = MIMEMultipart("form-data")
-    for name, content in data:
-        for payload in make_payloads(name, content):
-            message.attach(payload)
+    for name, contents in data:
+        if not isinstance(contents, list):
+            contents = [contents]
+        for content in contents:
+            for payload in make_payloads(name, content):
+                message.attach(payload)
     return message
 
 
diff --git a/_states/maas_cluster.py b/_states/maas_cluster.py
deleted file mode 100644
index e69de29..0000000
--- a/_states/maas_cluster.py
+++ /dev/null
diff --git a/_states/maasng.py b/_states/maasng.py
index 0275465..22c68fc 100644
--- a/_states/maasng.py
+++ b/_states/maasng.py
@@ -24,6 +24,23 @@
     return 'maasng'
 
 
+def maasng(funcname, *args, **kwargs):
+    """
+    Simple wrapper, for __salt__ maasng
+    :param funcname:
+    :param args:
+    :param kwargs:
+    :return:
+    """
+    return __salt__['maasng.{}'.format(funcname)](*args, **kwargs)
+
+
+def merge2dicts(d1, d2):
+    z = d1.copy()
+    z.update(d2)
+    return z
+
+
 def disk_layout_present(hostname, layout_type, root_size=None, root_device=None,
                         volume_group=None, volume_name=None, volume_size=None,
                         disk={}, **kwargs):
@@ -391,7 +408,9 @@
     return ret
 
 
-def boot_source_present(url, keyring_file='', keyring_data=''):
+def boot_source_present(url, keyring_file='', keyring_data='',
+                        delete_undefined_sources=False,
+                        delete_undefined_sources_except_urls=[]):
     """
     Process maas boot-sources: link to maas-ephemeral repo
 
@@ -399,6 +418,7 @@
     :param url:               The URL of the BootSource.
     :param keyring_file:      The path to the keyring file for this BootSource.
     :param keyring_data:      The GPG keyring for this BootSource, base64-encoded data.
+    :param delete_undefined_sources:  Delete all boot-sources, except defined in reclass
     """
     ret = {'name': url,
            'changes': {},
@@ -408,16 +428,20 @@
     if __opts__['test']:
         ret['result'] = None
         ret['comment'] = 'boot-source {0} will be updated'.format(url)
-
-    maas_boot_sources = __salt__['maasng.get_boot_source']()
-    # TODO imlpement check and update for keyrings!
+    maas_boot_sources = maasng('get_boot_source')
+    # TODO implement check and update for keyrings!
     if url in maas_boot_sources.keys():
         ret["result"] = True
         ret["comment"] = 'boot-source {0} alredy exist'.format(url)
-        return ret
-    ret["changes"] = __salt__['maasng.create_boot_source'](url,
-                                                           keyring_filename=keyring_file,
-                                                           keyring_data=keyring_data)
+    else:
+        ret["changes"] = maasng('create_boot_source', url,
+                                keyring_filename=keyring_file,
+                                keyring_data=keyring_data)
+    if delete_undefined_sources:
+        ret["changes"] = merge2dicts(ret.get('changes', {}),
+                                     maasng('boot_sources_delete_all_others',
+                                            except_urls=delete_undefined_sources_except_urls))
+        # Re-import data
     return ret
 
 
@@ -447,7 +471,7 @@
         ret['comment'] = 'boot-source {0}' \
                          'selection will be updated'.format(bs_url)
 
-    maas_boot_sources = __salt__['maasng.get_boot_source']()
+    maas_boot_sources = maasng('get_boot_source')
     if bs_url not in maas_boot_sources.keys():
         ret["result"] = False
         ret["comment"] = 'Requested boot-source' \
@@ -455,13 +479,11 @@
                          'to proceed selection for it'.format(bs_url)
         return ret
 
-    ret = __salt__['maasng.create_boot_source_selections'](bs_url,
-                                                           os,
-                                                           release,
-                                                           arches=arches,
-                                                           subarches=subarches,
-                                                           labels=labels,
-                                                           wait=wait)
+    ret = maasng('create_boot_source_selections', bs_url, os, release,
+                 arches=arches,
+                 subarches=subarches,
+                 labels=labels,
+                 wait=wait)
     return ret
 
 
diff --git a/maas/files/curtin_userdata_amd64_generic_trusty b/maas/files/curtin_userdata_amd64_generic_trusty
index b644934..0cc4a7d 100644
--- a/maas/files/curtin_userdata_amd64_generic_trusty
+++ b/maas/files/curtin_userdata_amd64_generic_trusty
@@ -25,7 +25,7 @@
 {% if cluster.saltstack_repo_trusty.startswith('deb') %}
   {%- set saltstack_repo = cluster.saltstack_repo_trusty -%}
 {%- else %}
-  {%- set saltstack_repo = 'deb [arch=amd64]' + cluster.saltstack_repo_trusty -%}
+  {%- set saltstack_repo = 'deb [arch=amd64] ' + cluster.saltstack_repo_trusty -%}
 {%- endif %}
   apt_01_set_repo: ["curtin", "in-target", "--", "sh", "-c", "echo '{{ saltstack_repo }}' >> /etc/apt/sources.list.d/mcp_saltstack.list"]
   apt_03_update: ["curtin", "in-target", "--", "apt-get", "update"]
diff --git a/maas/files/curtin_userdata_amd64_generic_xenial b/maas/files/curtin_userdata_amd64_generic_xenial
index 8c60529..82beaea 100644
--- a/maas/files/curtin_userdata_amd64_generic_xenial
+++ b/maas/files/curtin_userdata_amd64_generic_xenial
@@ -25,7 +25,7 @@
 {% if cluster.saltstack_repo_xenial.startswith('deb') %}
   {%- set saltstack_repo = cluster.saltstack_repo_xenial -%}
 {%- else %}
-  {%- set saltstack_repo = 'deb [arch=amd64]' + cluster.saltstack_repo_xenial -%}
+  {%- set saltstack_repo = 'deb [arch=amd64] ' + cluster.saltstack_repo_xenial -%}
 {%- endif %}
   apt_01_set_repo: ["curtin", "in-target", "--", "sh", "-c", "echo '{{ saltstack_repo }}' >> /etc/apt/sources.list.d/mcp_saltstack.list"]
   apt_03_update: ["curtin", "in-target", "--", "apt-get", "update"]
diff --git a/maas/files/curtin_userdata_arm64_generic_xenial b/maas/files/curtin_userdata_arm64_generic_xenial
index 3d6b34b..af9a047 100644
--- a/maas/files/curtin_userdata_arm64_generic_xenial
+++ b/maas/files/curtin_userdata_arm64_generic_xenial
@@ -25,7 +25,7 @@
 {% if cluster.saltstack_repo_xenial.startswith('deb') %}
   {%- set saltstack_repo = cluster.saltstack_repo_xenial -%}
 {%- else %}
-  {%- set saltstack_repo = 'deb [arch=amd64]' + cluster.saltstack_repo_xenial -%}
+  {%- set saltstack_repo = 'deb [arch=amd64] ' + cluster.saltstack_repo_xenial -%}
 {%- endif %}
 {#- NOTE: Re-use amd64 repos on arm64 since most packages are arch independent #}
   apt_01_set_repo: ["curtin", "in-target", "--", "sh", "-c", "echo '{{ saltstack_repo }}' >> /etc/apt/sources.list.d/mcp_saltstack.list"]
diff --git a/maas/mirror.sls b/maas/mirror.sls
index 0772e1c..26ae1db 100644
--- a/maas/mirror.sls
+++ b/maas/mirror.sls
@@ -9,6 +9,18 @@
     - names: {{ mirror.pkgs }}
 
 {%- for section_name, section in mirror.image.sections.iteritems() %}
+{%- if pillar.maas.region is defined and pillar.maas.region.maas_config.http_proxy is defined and pillar.maas.region.maas_config.get('enable_http_proxy', False) %}
+{%- set http_proxy = salt['pillar.get']('maas:region:maas_config').get('http_proxy', 'None') %}
+{%- set https_proxy = salt['pillar.get']('maas:region:maas_config').get('https_proxy', 'None') %}
+maas_mirror_proxy_{{ section_name }}:
+  environ.setenv:
+  - name: HTTP_PROXY
+  - value:
+      http_proxy: {{ http_proxy }}
+      https_proxy: {{ https_proxy }}
+  - require_in:
+    - pkg: mirror_image_{{ section_name }}
+{%- endif %}
 
 mirror_image_{{ section_name }}:
   cmd.run:
diff --git a/maas/region.sls b/maas/region.sls
index 1226b5d..fd54fb1 100644
--- a/maas/region.sls
+++ b/maas/region.sls
@@ -197,9 +197,6 @@
   - wait: True
   - require:
     - cmd: maas_login_admin
-  {% if region.get('boot_sources_delete_all_others', False)  %}
-    - module: region_boot_sources_delete_all_others
-  {%- endif %}
   - require_in:
     - module: maas_wait_for_racks_import_done
   {%- if grains.get('kitchen-test') %}
@@ -215,20 +212,6 @@
   - onlyif: /bin/false
   {%- endif %}
 
-{##}
-{% if region.get('boot_sources_delete_all_others', False)  %}
-  {# Collect exclude list, all other - will be removed #}
-  {% set exclude_list=[] %}
-  {%- for _, bs in region.boot_sources.iteritems() %} {% if bs.url is defined %} {% do exclude_list.append(bs.url) %} {% endif %} {%- endfor %}
-region_boot_sources_delete_all_others:
-  module.run:
-  - name: maasng.boot_sources_delete_all_others
-  - except_urls: {{ exclude_list }}
-  - require:
-    - cmd: maas_login_admin
-{%- endif %}
-
-{##}
 {% if region.get('boot_sources', False)  %}
   {%- for b_name, b_source in region.boot_sources.iteritems() %}
 maas_region_boot_source_{{ b_name }}:
@@ -240,6 +223,13 @@
   {%- if b_source.keyring_file is defined %}
     - keyring_file: {{ b_source.keyring_file }}
   {%- endif %}
+  {% if region.get('boot_sources_delete_all_others', False)  %}
+    {# Collect exclude list, all other - will be removed #}
+    {% set exclude_list=[] %}
+    {%- for _, bs in region.boot_sources.iteritems() %} {% if bs.url is defined %} {% do exclude_list.append(bs.url) %} {% endif %} {%- endfor %}
+    - delete_undefined_sources: True
+    - delete_undefined_sources_except_urls: {{ exclude_list }}
+  {%- endif %}
     - require:
       - cmd: maas_login_admin
   {%- endfor %}