Merge branch 'stable-2.12' into stable-2.13
[ganeti-github.git] / qa / qa_rapi.py
index 0fa030b..a008247 100644 (file)
 
 """
 
+import copy
 import functools
 import itertools
 import os.path
 import random
 import re
 import tempfile
+import uuid as uuid_module
 
 from ganeti import cli
 from ganeti import compat
@@ -312,6 +314,15 @@ JOB_FIELDS = compat.UniqueFrozenset([
   "received_ts", "start_ts", "end_ts",
   ])
 
+FILTER_FIELDS = compat.UniqueFrozenset([
+  "watermark",
+  "priority",
+  "predicates",
+  "action",
+  "reason_trail",
+  "uuid",
+  ])
+
 LIST_FIELDS = ("id", "uri")
 
 
@@ -464,6 +475,11 @@ def TestEmptyCluster():
       for field in GROUP_FIELDS:
         AssertIn(field, group)
 
+  def _VerifyFiltersBulk(data):
+    for group in data:
+      for field in FILTER_FIELDS:
+        AssertIn(field, group)
+
   _DoTests([
     ("/", None, "GET", None),
     ("/2/info", _VerifyInfo, "GET", None),
@@ -475,6 +491,8 @@ def TestEmptyCluster():
     ("/2/instances", [], "GET", None),
     ("/2/instances?bulk=1", [], "GET", None),
     ("/2/os", None, "GET", None),
+    ("/2/filters", [], "GET", None),
+    ("/2/filters?bulk=1", _VerifyFiltersBulk, "GET", None),
     ])
 
   # Test HTTP Not Found
@@ -527,15 +545,11 @@ def TestRapiQuery():
   rnd = random.Random(7818)
 
   for what in constants.QR_VIA_RAPI:
-    if what == constants.QR_JOB:
-      namefield = "id"
-      trivial_filter = [qlang.OP_GE, namefield, 0]
-    elif what == constants.QR_EXPORT:
-      namefield = "export"
-      trivial_filter = [qlang.OP_REGEXP, ".*", namefield]
-    else:
-      namefield = "name"
-      trivial_filter = [qlang.OP_REGEXP, ".*", namefield]
+    namefield = {
+      constants.QR_JOB: "id",
+      constants.QR_EXPORT: "export",
+      constants.QR_FILTER: "uuid",
+    }.get(what, "name")
 
     all_fields = query.ALL_FIELDS[what].keys()
     rnd.shuffle(all_fields)
@@ -566,7 +580,8 @@ def TestRapiQuery():
     # Try once more, this time without the client
     _DoTests([
       ("/2/query/%s/fields" % what, None, "GET", None),
-      ("/2/query/%s/fields?fields=name,name,%s" % (what, all_fields[0]),
+      ("/2/query/%s/fields?fields=%s,%s,%s" % (what, namefield, namefield,
+                                               all_fields[0]),
        None, "GET", None),
       ])
 
@@ -617,7 +632,11 @@ def TestRapiQuery():
            "fields": [namefield] * 4
          })])
 
-    def _CheckFilter():
+    if what in constants.QR_VIA_RAPI_PUT:
+      trivial_filter = {
+        constants.QR_JOB: [qlang.OP_GE, namefield, 0],
+      }.get(what, [qlang.OP_REGEXP, namefield, ".*"])
+
       _DoTests([
         # With filter
         ("/2/query/%s" % what, compat.partial(_Check, all_fields), "PUT", {
@@ -626,18 +645,6 @@ def TestRapiQuery():
            }),
         ])
 
-    if what == constants.QR_LOCK:
-      # Locks can't be filtered
-      try:
-        _CheckFilter()
-      except rapi.client.GanetiApiError, err:
-        AssertEqual(err.code, 500)
-      else:
-        raise qa_error.Error("Filtering locks didn't fail")
-    else:
-      if what in constants.QR_VIA_RAPI_PUT:
-        _CheckFilter()
-
     if what == constants.QR_NODE:
       # Test with filter
       (nodes, ) = _DoTests(
@@ -1254,6 +1261,54 @@ def TestInterClusterInstanceMove(src_instance, dest_instance,
                       target_nodes=(pnode.primary, snode.primary))
 
 
+def TestFilters():
+  """Testing filter management via the remote API.
+
+  """
+
+  body = {
+    "priority": 10,
+    "predicates": [],
+    "action": "CONTINUE",
+    "reason": [(constants.OPCODE_REASON_SRC_USER,
+               "reason1",
+               utils.EpochNano())],
+  }
+
+  body1 = copy.deepcopy(body)
+  body1["priority"] = 20
+
+  # Query filters
+  _DoTests([("/2/filters", [], "GET", None)])
+
+  # Add a filter via POST and delete it again
+  uuid = _DoTests([("/2/filters", None, "POST", body)])[0]
+  uuid_module.UUID(uuid)  # Check if uuid is a valid UUID
+  _DoTests([("/2/filters/%s" % uuid, lambda r: r is None, "DELETE", None)])
+
+  _DoTests([
+    # Check PUT-inserting a nonexistent filter with given UUID
+    ("/2/filters/%s" % uuid, lambda u: u == uuid, "PUT", body),
+    # Check PUT-inserting an existent filter with given UUID
+    ("/2/filters/%s" % uuid, lambda u: u == uuid, "PUT", body1),
+    # Check that the update changed the filter
+    ("/2/filters/%s" % uuid, lambda f: f["priority"] == 20, "GET", None),
+    # Delete it again
+    ("/2/filters/%s" % uuid, lambda r: r is None, "DELETE", None),
+    ])
+
+  # Add multiple filters, query and delete them
+  uuids = _DoTests([
+    ("/2/filters", None, "POST", body),
+    ("/2/filters", None, "POST", body),
+    ("/2/filters", None, "POST", body),
+    ])
+  _DoTests([("/2/filters", lambda rs: [r["uuid"] for r in rs] == uuids,
+             "GET", None)])
+  for u in uuids:
+    _DoTests([("/2/filters/%s" % u, lambda r: r is None, "DELETE", None)])
+
+
 _DRBD_SECRET_RE = re.compile('shared-secret.*"([0-9A-Fa-f]+)"')