[WIP]: Add quota module
diff --git a/staffeln/common/openstack.py b/staffeln/common/openstack.py
index c4a28c1..02570c9 100644
--- a/staffeln/common/openstack.py
+++ b/staffeln/common/openstack.py
@@ -1,3 +1,5 @@
+from openstack import exceptions

+from openstack import proxy

 from staffeln.common import auth

 

 conn = auth.create_connection()

@@ -51,3 +53,34 @@
     # )

     conn.delete_volume_backup(uuid, force=force)

     # TODO(Alex): After delete the backup generator, need to set the volume status again

+

+

+def get_backup_quota(project_id):

+    # quota = conn.get_volume_quotas(project_id)

+    quota = _get_volume_quotas(project_id)

+    return quota.backups

+

+

+# rewrite openstasdk._block_storage.get_volume_quotas

+# added usage flag

+# ref: https://docs.openstack.org/api-ref/block-storage/v3/?expanded=#show-quota-usage-for-a-project

+def _get_volume_quotas(project_id, usage=True):

+    """ Get volume quotas for a project

+

+    :param name_or_id: project name or id

+    :raises: OpenStackCloudException if it's not a valid project

+

+    :returns: Munch object with the quotas

+    """

+

+    if usage:

+        resp = conn.block_storage.get(

+            '/os-quota-sets/{project_id}?usage=True'.format(project_id=project_id))

+    else:

+        resp = conn.block_storage.get(

+            '/os-quota-sets/{project_id}'.format(project_id=project_id))

+    data = proxy._json_response(

+        resp,

+        error_message="cinder client call failed")

+    return conn._get_and_munchify('quota_set', data)

+

diff --git a/staffeln/conductor/backup.py b/staffeln/conductor/backup.py
index 5371f39..bcdc5b8 100755
--- a/staffeln/conductor/backup.py
+++ b/staffeln/conductor/backup.py
@@ -42,6 +42,9 @@
     def get_backups(self, filters=None):
         return objects.Volume.list(self.ctx, filters=filters)
 
+    def get_backup_quota(self, project_id):
+        return openstacksdk.get_backup_quota(project_id)
+
     def get_queues(self, filters=None):
         """Get the list of volume queue columns from the queue_data table"""
         queues = objects.Queue.list(self.ctx, filters=filters)
@@ -62,7 +65,7 @@
                 self._volume_queue(queue)
 
     # Backup the volumes attached to which has a specific metadata
-    def filter_server(self, metadata):
+    def filter_by_server_metadata(self, metadata):
 
         if not CONF.conductor.backup_metadata_key in metadata:
             return False
@@ -70,7 +73,7 @@
         return metadata[CONF.conductor.backup_metadata_key].lower() == constants.BACKUP_ENABLED_KEY
 
     # Backup the volumes in in-use and available status
-    def filter_volume(self, volume_id):
+    def filter_by_volume_status(self, volume_id):
         try:
             volume = openstacksdk.get_volume(volume_id)
             if volume == None: return False
@@ -169,9 +172,9 @@
         for project in projects:
             servers = openstacksdk.get_servers(project_id=project.id)
             for server in servers:
-                if not self.filter_server(server.metadata): continue
+                if not self.filter_by_server_metadata(server.metadata): continue
                 for volume in server.attached_volumes:
-                    if not self.filter_volume(volume["id"]): continue
+                    if not self.filter_by_volume_status(volume["id"]): continue
                     queues_map.append(
                         QueueMapping(
                             project_id=project.id,
diff --git a/staffeln/conductor/manager.py b/staffeln/conductor/manager.py
index e36265d..e68c3fe 100755
--- a/staffeln/conductor/manager.py
+++ b/staffeln/conductor/manager.py
@@ -43,20 +43,6 @@
     def reload(self):

         LOG.info("%s reload" % self.name)

 

-    # Check if the backup count is over the limit

-    # TODO(Alex): how to count the backup number

-    #  only available backups are calculated?

-    def _check_quota(self):

-        LOG.info(_("Checking the backup limitation..."))

-        max_count = CONF.conductor.max_backup_count

-        current_count = len(backup.Backup().get_backups())

-        if max_count <= current_count:

-            # TODO(Alex): Send notification

-            LOG.info(_("The backup limit is over."))

-            return True

-        LOG.info(_("The max limit is %s, and current backup count is %s" % (max_count, current_count)))

-        return False

-

     # Manage active backup generators

     def _process_wip_tasks(self):

         LOG.info(_("Processing WIP backup generators..."))

@@ -121,23 +107,24 @@
         backup.Backup().create_queue(current_tasks)

 

     def _report_backup_result(self):

-        # TODO(Alex): Need to update these list

+        # 1. get the quota usage

+        # 2. get the success backup list

+        # 3. get the failed backup list

+        # 4. send notification

+        quota = backup.Backup().get_backup_quota()

         self.success_backup_list = []

         self.failed_backup_list = []

-        notify.SendBackupResultEmail(self.success_backup_list, self.failed_backup_list)

+        notify.SendBackupResultEmail(quota, self.success_backup_list, self.failed_backup_list)

 

     @periodics.periodic(spacing=CONF.conductor.backup_service_period, run_immediately=True)

     def backup_engine(self):

         LOG.info("backing... %s" % str(time.time()))

         LOG.info("%s periodics" % self.name)

 

-        if self._check_quota(): return

-        # NOTE(Alex): If _process_wip_tasks() waits tiil no WIP tasks

-        # exist, no need to repeat this function before and after queue update.

         self._update_task_queue()

         self._process_todo_tasks()

         self._process_wip_tasks()

-        # self._report_backup_result()

+        self._report_backup_result()

 

 

 class RotationManager(cotyledon.Service):

diff --git a/staffeln/conductor/notify.py b/staffeln/conductor/notify.py
index 2f77aa9..6d76368 100644
--- a/staffeln/conductor/notify.py
+++ b/staffeln/conductor/notify.py
@@ -8,6 +8,9 @@
 from staffeln.common import time as xtime

 from staffeln.i18n import _

 

+__DEBUG__ = True

+

+

 CONF = staffeln.conf.CONF

 LOG = log.getLogger(__name__)

 

@@ -18,8 +21,10 @@
     message["From"] = src_email

     message["To"] = dest_email

     part = MIMEText(content, "html")

+    print(part)

     message.attach(part)

-

+    if __DEBUG__:

+        return

     s = smtplib.SMTP(host=smtp_server_domain, port=smtp_server_port)

     s.ehlo()

     s.starttls()

@@ -29,18 +34,23 @@
     s.close()

 

 

-def SendBackupResultEmail(success_backup_list, failed_backup_list):

+def SendBackupResultEmail(quota, success_backup_list, failed_backup_list):

     subject = "Backup result"

 

-    html = "<h3>${TIME}</h3>" \

-           "<h3>Success List</h3>" \

-           "<h4>${SUCCESS_VOLUME_LIST}</h4>" \

-           "<h3>Failed List</h3>" \

-           "<h4>${FAILED_VOLUME_LIST}</h4>"

+    html = "<h3>${TIME}</h3><br>" \

+           "<h3>Quota Usage</h3><br>" \

+           "<h4>Limit: ${QUOTA_LIMIT}, In Use: ${QUOTA_IN_USE}, Reserved: ${QUOTA_RESERVED}</h4><br>" \

+           "<h3>Success List</h3><br>" \

+           "<h4>${SUCCESS_VOLUME_LIST}</h4><br>" \

+           "<h3>Failed List</h3><br>" \

+           "<h4>${FAILED_VOLUME_LIST}</h4><br>"

 

     success_volumes = '<br>'.join([str(elem) for elem in success_backup_list])

     failed_volumes = '<br>'.join([str(elem) for elem in failed_backup_list])

     html = html.replace("${TIME}", xtime.get_current_strtime())

+    html = html.replace("${QUOTA_LIMIT}", quota["limit"])

+    html = html.replace("${QUOTA_IN_USE}", quota["in_use"])

+    html = html.replace("${QUOTA_RESERVED}", quota["reserved"])

     html = html.replace("${SUCCESS_VOLUME_LIST}", success_volumes)

     html = html.replace("${FAILED_VOLUME_LIST}", failed_volumes)

     try:

diff --git a/staffeln/conductor/result.py b/staffeln/conductor/result.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/staffeln/conductor/result.py
diff --git a/staffeln/conf/conductor.py b/staffeln/conf/conductor.py
index 7c9c643..d5e3861 100755
--- a/staffeln/conf/conductor.py
+++ b/staffeln/conf/conductor.py
@@ -33,11 +33,6 @@
         default="__automated_backup",
         help=_("The key string of metadata the VM, which requres back up, has"),
     ),
-    cfg.IntOpt(
-        "max_backup_count",
-        default=10,
-        help=_("The key string of metadata the VM, which requres back up, has"),
-    ),
 ]
 
 rotation_opts = [