Add Virtual Network Interface REST APIs

This patch adds the REST APIs for the virtual network interface API in
order to abstract the task of assigning logical network interfaces to
physical network interfaces.

Since Newton Ironic provides an interface for pluggable network
implementations. Different network implementations may want to handle
how logical to physical network interface assignment happens. To do this
the new API calls into new functions on the network implementation
loaded for the specified node.

This is part 3 of 3, and adds the node vif subcontroller to expose the
/nodes/<ident>/vifs REST API endpoint. API version is bumped to 1.28.

Co-Authored-By: Vasyl Saienko (vsaienko@mirantis.com)
Change-Id: I70f1166a15a26f392734e21d6bc30a03da4e5486
Partial-Bug: #1582188
diff --git a/ironic_tempest_plugin/services/baremetal/base.py b/ironic_tempest_plugin/services/baremetal/base.py
index 67e4d08..30589e1 100644
--- a/ironic_tempest_plugin/services/baremetal/base.py
+++ b/ironic_tempest_plugin/services/baremetal/base.py
@@ -115,10 +115,13 @@
 
         return patch
 
-    def _list_request(self, resource, permanent=False, **kwargs):
+    def _list_request(self, resource, permanent=False, headers=None,
+                      extra_headers=False, **kwargs):
         """Get the list of objects of the specified type.
 
         :param resource: The name of the REST resource, e.g., 'nodes'.
+        :param headers: List of headers to use in request.
+        :param extra_headers: Specify whether to use headers.
         :param **kwargs: Parameters for the request.
         :returns: A tuple with the server response and deserialized JSON list
                  of objects
@@ -128,7 +131,8 @@
         if kwargs:
             uri += "?%s" % urllib.urlencode(kwargs)
 
-        resp, body = self.get(uri)
+        resp, body = self.get(uri, headers=headers,
+                              extra_headers=extra_headers)
         self.expected_success(200, resp.status)
 
         return resp, self.deserialize(body)
@@ -167,6 +171,25 @@
 
         return resp, self.deserialize(body)
 
+    def _create_request_no_response_body(self, resource, object_dict):
+        """Create an object of the specified type.
+
+           Do not expect any body in the response.
+
+        :param resource: The name of the REST resource, e.g., 'nodes'.
+        :param object_dict: A Python dict that represents an object of the
+                            specified type.
+        :returns: The server response.
+        """
+
+        body = self.serialize(object_dict)
+        uri = self._get_uri(resource)
+
+        resp, body = self.post(uri, body=body)
+        self.expected_success(204, resp.status)
+
+        return resp
+
     def _delete_request(self, resource, uuid):
         """Delete specified object.