Merge remote-tracking branch 'target/master'
diff --git a/README.rst b/README.rst
index 1e1b003..9e0d472 100644
--- a/README.rst
+++ b/README.rst
@@ -153,9 +153,11 @@
         - 'ssh-rsa ASDFOSADFISdfasdfasjdklfjasdJFASDJfASdf923@AAAAB3NzaC1yc2EAAAADAQABAAACAQCv8ISOESGgYUOycYw1SAs/SfHTqtSCTephD/7o2+mEZO53xN98sChiFscFaPA2ZSMoZbJ6MQLKcWKMK2OaTdNSAvn4UE4T6VP0ccdumHDNRwO3f6LptvXr9NR5Wocz2KAgptk+uaA8ytM0Aj9NT0UlfjAXkKnoKyNq6yG+lx4HpwolVaFSlqRXf/iuHpCrspv/u1NW7ReMElJoXv+0zZ7Ow0ZylISdYkaqbV8QatCb17v1+xX03xLsZigfugce/8CDsibSYvJv+Hli5CCBsKgfFqLy4R5vGxiLSVzG/asdjalskjdlkasjdasd/asdajsdkjalaksdjfasd/fa/sdf/asd/fas/dfsadf blah@blah'
 
 
+
 Usage of local repos
 
 .. code-block:: yaml
+
   maas:
     cluster:
       enabled: true
@@ -206,6 +208,60 @@
         enabled: true
         role: master/slave
 
+.. code-block:: yaml
+
+    maas:
+      cluster:
+        enabled: true
+        role: master/slave
+
+Module function's example:
+==========================
+
+* Wait for status of selected machine's:
+
+.. code-block:: bash
+
+    > cat maas/machines/wait_for_machines_ready.sls
+
+    ...
+
+    wait_for_machines_ready:
+      module.run:
+      - name: maas.wait_for_machine_status
+      - kwargs:
+            machines:
+              - kvm01
+              - kvm02
+            timeout: 1200 # in seconds
+            req_status: "Ready"
+      - require:
+        - cmd: maas_login_admin
+      ...
+
+If module run w/\o any extra paremeters - `wait_for_machines_ready` will wait for defined in salt machines. In those case, will be usefull to skip some machines:
+
+.. code-block:: bash
+
+    > cat maas/machines/wait_for_machines_deployed.sls
+
+    ...
+
+    wait_for_machines_ready:
+      module.run:
+      - name: maas.wait_for_machine_status
+      - kwargs:
+            timeout: 1200 # in seconds
+            req_status: "Deployed"
+            ignore_machines:
+               - kvm01 # in case it's broken or whatever
+      - require:
+        - cmd: maas_login_admin
+      ...
+
+List of avaibled `req_status` defined in global variable:
+
+
 Read more
 =========
 
diff --git a/_modules/maas.py b/_modules/maas.py
index 5907068..e279b70 100644
--- a/_modules/maas.py
+++ b/_modules/maas.py
@@ -13,15 +13,15 @@
 
 from __future__ import absolute_import
 
-import io
-import logging
 import collections
-import os.path
-import subprocess
-import urllib2
+import copy
 import hashlib
-
+import io
 import json
+import logging
+import os.path
+import time
+import urllib2
 
 LOG = logging.getLogger(__name__)
 
@@ -46,6 +46,14 @@
 
 APIKEY_FILE = '/var/lib/maas/.maas_credentials'
 
+STATUS_NAME_DICT = dict([
+    (0, 'New'), (1, 'Commissioning'), (2, 'Failed commissioning'),
+    (3, 'Missing'), (4, 'Ready'), (5, 'Reserved'), (10, 'Allocated'),
+    (9, 'Deploying'), (6, 'Deployed'), (7, 'Retired'), (8, 'Broken'),
+    (11, 'Failed deployment'), (12, 'Releasing'),
+    (13, 'Releasing failed'), (14, 'Disk erasing'),
+    (15, 'Failed disk erasing')])
+
 
 def _format_data(data):
     class Lazy:
@@ -409,7 +417,7 @@
         if machine['status'] == self.DEPLOYED:
             return
         if machine['status'] != self.READY:
-            raise Exception('Not in ready state')
+            raise Exception('Machine:{} not in READY state'.format(name))
         if 'ip' not in interface:
             return
         data = {
@@ -426,6 +434,7 @@
 
 
 class DeployMachines(MaasObject):
+    # FIXME
     READY = 4
     DEPLOYED = 6
 
@@ -656,13 +665,6 @@
         result = cls._maas.get(u'api/2.0/machines/')
         json_result = json.loads(result.read())
         res = []
-        status_name_dict = dict([
-            (0, 'New'), (1, 'Commissioning'), (2, 'Failed commissioning'),
-            (3, 'Missing'), (4, 'Ready'), (5, 'Reserved'), (10, 'Allocated'),
-            (9, 'Deploying'), (6, 'Deployed'), (7, 'Retired'), (8, 'Broken'),
-            (11, 'Failed deployment'), (12, 'Releasing'),
-            (13, 'Releasing failed'), (14, 'Disk erasing'),
-            (15, 'Failed disk erasing')])
         summary = collections.Counter()
         if objects_name:
             if ',' in objects_name:
@@ -672,7 +674,7 @@
         for machine in json_result:
             if objects_name and machine['hostname'] not in objects_name:
                 continue
-            status = status_name_dict[machine['status']]
+            status = STATUS_NAME_DICT[machine['status']]
             summary[status] += 1
             res.append(
                 {'hostname': machine['hostname'],
@@ -680,6 +682,68 @@
                  'status': status})
         return {'machines': res, 'summary': summary}
 
+    @classmethod
+    def wait_for_machine_status(cls, **kwargs):
+        """
+        A function that wait for any requested status, for any set of maas
+        machines.
+
+        If no kwargs has been passed - will try to wait ALL
+        defined in salt::maas::region::machines
+
+        See readme file for more examples.
+        CLI Example:
+        .. code-block:: bash
+
+            salt-call state.apply maas.machines.wait_for_deployed
+
+        :param kwargs:
+            timeout:    in s; Global timeout for wait
+            poll_time:  in s;Sleep time, between retry
+            req_status: string; Polling status
+            machines:   list; machine names
+            ignore_machines: list; machine names
+        :ret: True
+                 Exception - if something fail/timeout reached
+        """
+        timeout = kwargs.get("timeout", 60 * 120)
+        poll_time = kwargs.get("poll_time", 30)
+        req_status = kwargs.get("req_status", "Ready")
+        to_discover = kwargs.get("machines", None)
+        ignore_machines = kwargs.get("ignore_machines", None)
+        if not to_discover:
+            try:
+                to_discover = __salt__['config.get']('maas')['region'][
+                    'machines'].keys()
+            except KeyError:
+                LOG.warning("No defined machines!")
+                return True
+        total = copy.deepcopy(to_discover) or []
+        if ignore_machines and total:
+            total = [x for x in to_discover if x not in ignore_machines]
+        started_at = time.time()
+        while len(total) <= len(to_discover):
+            for m in to_discover:
+                for discovered in MachinesStatus.execute()['machines']:
+                    if m == discovered['hostname'] and \
+                            discovered['status'].lower() == req_status.lower():
+                        if m in total:
+                            total.remove(m)
+
+            if len(total) <= 0:
+                LOG.debug(
+                    "Machines:{} are:{}".format(to_discover, req_status))
+                return True
+            if (timeout - (time.time() - started_at)) <= 0:
+                raise Exception(
+                    'Machines:{}not in {} state'.format(total, req_status))
+            LOG.info(
+                "Waiting status:{} "
+                "for machines:{}"
+                "\nsleep for:{}s "
+                "Timeout:{}s".format(req_status, total, poll_time, timeout))
+            time.sleep(poll_time)
+
 
 def process_fabrics():
     return Fabric().process()
@@ -735,3 +799,7 @@
 
 def process_sshprefs():
     return SSHPrefs().process()
+
+
+def wait_for_machine_status(**kwargs):
+    return MachinesStatus.wait_for_machine_status(**kwargs)
diff --git a/maas/files/pgpass b/maas/files/pgpass
new file mode 100644
index 0000000..daca0c7
--- /dev/null
+++ b/maas/files/pgpass
@@ -0,0 +1,2 @@
+{%- from "maas/map.jinja" import region with context %}
+{{ region.database.host }}:5432:{{ region.database.name }}:{{ region.database.username }}:{{ region.database.password }}
\ No newline at end of file
diff --git a/maas/machines/wait_for_deployed.sls b/maas/machines/wait_for_deployed.sls
new file mode 100644
index 0000000..ebeedac
--- /dev/null
+++ b/maas/machines/wait_for_deployed.sls
@@ -0,0 +1,13 @@
+{%- from "maas/map.jinja" import region with context %}
+
+maas_login_admin:
+  cmd.run:
+  - name: "maas-region apikey --username {{ region.admin.username }} > /var/lib/maas/.maas_credentials"
+
+wait_for_machines_deployed:
+  module.run:
+  - name: maas.wait_for_machine_status
+  - kwargs:
+        req_status: "Deployed"
+  - require:
+    - cmd: maas_login_admin
diff --git a/maas/machines/wait_for_ready.sls b/maas/machines/wait_for_ready.sls
new file mode 100644
index 0000000..c5d3c28
--- /dev/null
+++ b/maas/machines/wait_for_ready.sls
@@ -0,0 +1,11 @@
+{%- from "maas/map.jinja" import region with context %}
+
+maas_login_admin:
+  cmd.run:
+  - name: "maas-region apikey --username {{ region.admin.username }} > /var/lib/maas/.maas_credentials"
+
+wait_for_machines_ready:
+  module.run:
+  - name: maas.wait_for_machine_status
+  - require:
+    - cmd: maas_login_admin
diff --git a/maas/meta/backupninja.yml b/maas/meta/backupninja.yml
new file mode 100644
index 0000000..36a18af
--- /dev/null
+++ b/maas/meta/backupninja.yml
@@ -0,0 +1,8 @@
+backup:
+  maas:
+    fs_includes:
+    - /etc/maas
+    - /var/lib/maas
+    - /var/backups/postgresql
+    fs_excludes:
+    - /var/lib/maas/boot-resources
diff --git a/maas/region.sls b/maas/region.sls
index a83003f..3b88307 100644
--- a/maas/region.sls
+++ b/maas/region.sls
@@ -78,6 +78,14 @@
   - require:
     - pkg: maas_region_packages
 
+/root/.pgpass:
+  file.managed:
+  - source: salt://maas/files/pgpass
+  - template: jinja
+  - user: root
+  - group: root
+  - mode: 600
+
 maas_region_services:
   service.running:
   - enable: true
diff --git a/metadata/service/support.yml b/metadata/service/support.yml
index 09b1d97..5085e28 100644
--- a/metadata/service/support.yml
+++ b/metadata/service/support.yml
@@ -9,3 +9,5 @@
         enabled: false
       sphinx:
         enabled: true
+      backupninja:
+        enabled: true