DSCP QoS rule implementation

This patch adds the front end and back end implementation of QoS DSCP.

Associated patches that are dependent on this one:

* python-neutronclient: https://review.openstack.org/#/c/254280
* openstack-manuals: https://review.openstack.org/#/c/273638
* API Guide: https://review.openstack.org/#/c/275253
* Heat:
  * Spec: https://review.openstack.org/#/c/272173
  * QoSDscpMarkingRule resource: https://review.openstack.org/#/c/277567
* Fullstack tests: https://review.openstack.org/#/c/288392/

APIImpact - The API now supports marking traffic egressing from a VM's
            dscp field with a valid dscp value.

Co-Authored-By: Nate Johnston <nate_johnston@cable.comcast.com>
Co-Authored-By: Victor Howard <victor.r.howard@gmail.com>
Co-Authored-By: Margaret Frances <margaret_frances@cable.comcast.com>
Co-Authored-By: James Reeves <james.reeves5546@gmail.com>
Co-Authored-By: John Schwarz <jschwarz@redhat.com>
Needed-By: I25ad60c1b9a66e568276a772b8c496987d9f8299
Needed-By: I881b8f5bc9024c20275bc56062de72a1c70c8321
Needed-By: I48ead4b459183db795337ab729830a1b3c0022da
Needed-By: Ib92b172dce48276b90ec75ee5880ddd69040d7c8
Needed-By: I4eb21495e84feea46880caf3360759263e1e8f95
Needed-By: I0ab6a1a0d1430c5791fea1d5b54106c6cc93b937
Partial-Bug: #1468353

Change-Id: Ic3baefe176df05f049a2e06529c58fd65fe6b419
diff --git a/neutron/tests/tempest/services/network/json/network_client.py b/neutron/tests/tempest/services/network/json/network_client.py
index 0795d58..18f33e7 100644
--- a/neutron/tests/tempest/services/network/json/network_client.py
+++ b/neutron/tests/tempest/services/network/json/network_client.py
@@ -699,6 +699,50 @@
         self.expected_success(204, resp.status)
         return service_client.ResponseBody(resp, body)
 
+    def create_dscp_marking_rule(self, policy_id, dscp_mark):
+        uri = '%s/qos/policies/%s/dscp_marking_rules' % (
+            self.uri_prefix, policy_id)
+        post_data = self.serialize(
+            {'dscp_marking_rule': {
+                'dscp_mark': dscp_mark}
+            })
+        resp, body = self.post(uri, post_data)
+        self.expected_success(201, resp.status)
+        body = json.loads(body)
+        return service_client.ResponseBody(resp, body)
+
+    def list_dscp_marking_rules(self, policy_id):
+        uri = '%s/qos/policies/%s/dscp_marking_rules' % (
+            self.uri_prefix, policy_id)
+        resp, body = self.get(uri)
+        body = self.deserialize_single(body)
+        self.expected_success(200, resp.status)
+        return service_client.ResponseBody(resp, body)
+
+    def show_dscp_marking_rule(self, policy_id, rule_id):
+        uri = '%s/qos/policies/%s/dscp_marking_rules/%s' % (
+            self.uri_prefix, policy_id, rule_id)
+        resp, body = self.get(uri)
+        body = self.deserialize_single(body)
+        self.expected_success(200, resp.status)
+        return service_client.ResponseBody(resp, body)
+
+    def update_dscp_marking_rule(self, policy_id, rule_id, **kwargs):
+        uri = '%s/qos/policies/%s/dscp_marking_rules/%s' % (
+            self.uri_prefix, policy_id, rule_id)
+        post_data = {'dscp_marking_rule': kwargs}
+        resp, body = self.put(uri, json.dumps(post_data))
+        body = self.deserialize_single(body)
+        self.expected_success(200, resp.status)
+        return service_client.ResponseBody(resp, body)
+
+    def delete_dscp_marking_rule(self, policy_id, rule_id):
+        uri = '%s/qos/policies/%s/dscp_marking_rules/%s' % (
+            self.uri_prefix, policy_id, rule_id)
+        resp, body = self.delete(uri)
+        self.expected_success(204, resp.status)
+        return service_client.ResponseBody(resp, body)
+
     def list_qos_rule_types(self):
         uri = '%s/qos/rule-types' % self.uri_prefix
         resp, body = self.get(uri)