Adds placement resource provider traits API calls

Change-Id: Iaaff5fa36a6559d30a64c8679a0c1852f9e00342
diff --git a/releasenotes/notes/add-placement-resource-provider-traits-api-calls-9f4b0455afec9afb.yaml b/releasenotes/notes/add-placement-resource-provider-traits-api-calls-9f4b0455afec9afb.yaml
new file mode 100644
index 0000000..1d1811c
--- /dev/null
+++ b/releasenotes/notes/add-placement-resource-provider-traits-api-calls-9f4b0455afec9afb.yaml
@@ -0,0 +1,4 @@
+---
+features:
+  - |
+    Adds API calls for traits in ResourceProvidersClient.
diff --git a/tempest/lib/services/placement/resource_providers_client.py b/tempest/lib/services/placement/resource_providers_client.py
index 3214053..a336500 100644
--- a/tempest/lib/services/placement/resource_providers_client.py
+++ b/tempest/lib/services/placement/resource_providers_client.py
@@ -121,3 +121,29 @@
         resp, body = self.delete(url)
         self.expected_success(204, resp.status)
         return rest_client.ResponseBody(resp, body)
+
+    def list_resource_provider_traits(self, rp_uuid, **kwargs):
+        """https://docs.openstack.org/api-ref/placement/#resource-provider-traits
+        """
+        url = f"/resource_providers/{rp_uuid}/traits"
+        if kwargs:
+            url += '?%s' % urllib.urlencode(kwargs)
+        resp, body = self.get(url)
+        self.expected_success(200, resp.status)
+        body = json.loads(body)
+        return rest_client.ResponseBody(resp, body)
+
+    def update_resource_provider_traits(self, rp_uuid, **kwargs):
+        url = f"/resource_providers/{rp_uuid}/traits"
+        data = json.dumps(kwargs)
+        resp, body = self.put(url, data)
+        self.expected_success(200, resp.status)
+        body = json.loads(body)
+        return rest_client.ResponseBody(resp, body)
+
+    def delete_resource_provider_traits(self, rp_uuid):
+        url = f"/resource_providers/{rp_uuid}/traits"
+        resp, body = self.delete(url)
+        self.expected_success(204, resp.status)
+        body = json.loads(body)
+        return rest_client.ResponseBody(resp, body)
diff --git a/tempest/tests/lib/services/placement/test_resource_providers_client.py b/tempest/tests/lib/services/placement/test_resource_providers_client.py
index 2871395..399f323 100644
--- a/tempest/tests/lib/services/placement/test_resource_providers_client.py
+++ b/tempest/tests/lib/services/placement/test_resource_providers_client.py
@@ -204,3 +204,40 @@
 
     def test_show_resource_provider_usages_with_with_bytes_body(self):
         self._test_list_resource_provider_inventories(bytes_body=True)
+
+    FAKE_ALL_RESOURCE_PROVIDER_TRAITS = {
+        "resource_provider_generation": 0,
+        "traits": [
+            "CUSTOM_HW_FPGA_CLASS1",
+            "CUSTOM_HW_FPGA_CLASS2"
+        ]
+    }
+    FAKE_NEW_RESOURCE_PROVIDER_TRAITS = {
+        "resource_provider_generation": 1,
+        "traits": [
+            "CUSTOM_HW_FPGA_CLASS1",
+            "CUSTOM_HW_FPGA_CLASS2"
+        ]
+    }
+
+    def test_list_resource_provider_traits(self):
+        self.check_service_client_function(
+            self.client.list_resource_provider_traits,
+            'tempest.lib.common.rest_client.RestClient.get',
+            self.FAKE_ALL_RESOURCE_PROVIDER_TRAITS,
+            rp_uuid=self.FAKE_RESOURCE_PROVIDER_UUID)
+
+    def test_update_resource_provider_traits(self):
+        self.check_service_client_function(
+            self.client.update_resource_provider_traits,
+            'tempest.lib.common.rest_client.RestClient.put',
+            self.FAKE_NEW_RESOURCE_PROVIDER_TRAITS,
+            rp_uuid=self.FAKE_RESOURCE_PROVIDER_UUID,
+            **self.FAKE_NEW_RESOURCE_PROVIDER_TRAITS)
+
+    def test_delete_resource_provider_traits(self):
+        self.check_service_client_function(
+            self.client.delete_resource_provider_traits,
+            'tempest.lib.common.rest_client.RestClient.delete',
+            self.FAKE_ALL_RESOURCE_PROVIDER_TRAITS, status=204,
+            rp_uuid=self.FAKE_RESOURCE_PROVIDER_UUID)