Test graphical console novnc connection

This creates a fake-graphical console and confirms that the websocket
returns the first part of an RFB handshake. For the systemd console
container provider this confirms that the container is running and
listening for incoming VNC connections. This is the same level of test
coverage that nova-novncproxy has in tempest.

Change-Id: Iad118cf2d031125450c00f83b7edfc3f0cedbd1a
Signed-off-by: Steve Baker <sbaker@redhat.com>
diff --git a/ironic_tempest_plugin/config.py b/ironic_tempest_plugin/config.py
index 38a67eb..b0f44d2 100644
--- a/ironic_tempest_plugin/config.py
+++ b/ironic_tempest_plugin/config.py
@@ -193,6 +193,9 @@
     cfg.ListOpt('enabled_bios_interfaces',
                 default=['fake'],
                 help="List of Ironic enabled bios interfaces."),
+    cfg.ListOpt('enabled_console_interfaces',
+                default=['fake', 'no-console'],
+                help="List of Ironic enabled console interfaces."),
     cfg.ListOpt('enabled_deploy_interfaces',
                 default=['iscsi', 'direct'],
                 help="List of Ironic enabled deploy interfaces."),
diff --git a/ironic_tempest_plugin/services/baremetal/v1/json/baremetal_client.py b/ironic_tempest_plugin/services/baremetal/v1/json/baremetal_client.py
index d12ece4..3a7e35d 100644
--- a/ironic_tempest_plugin/services/baremetal/v1/json/baremetal_client.py
+++ b/ironic_tempest_plugin/services/baremetal/v1/json/baremetal_client.py
@@ -299,12 +299,13 @@
 
         """
         node = {}
-        # Explicitly allow definition of network interface and deploy
-        # interface to allow tests to specify the required values
-        # as they hold a great deal of logic which is executed upon and
-        # they can ultimately impact test behavior.
+        # Explicitly allow definition of network interface, deploy
+        # interface, and console interface to allow tests to specify
+        # the required values as they hold a great deal of logic which
+        # is executed upon and they can ultimately impact test behavior.
         for field in ('resource_class', 'name', 'description', 'shard',
-                      'network_interface', 'deploy_interface'):
+                      'network_interface', 'deploy_interface',
+                      'console_interface'):
             if kwargs.get(field):
                 node[field] = kwargs[field]
 
diff --git a/ironic_tempest_plugin/tests/api/admin/test_nodes.py b/ironic_tempest_plugin/tests/api/admin/test_nodes.py
index 0d82577..27d87cf 100644
--- a/ironic_tempest_plugin/tests/api/admin/test_nodes.py
+++ b/ironic_tempest_plugin/tests/api/admin/test_nodes.py
@@ -10,7 +10,9 @@
 #    License for the specific language governing permissions and limitations
 #    under the License.
 
+
 from oslo_utils import uuidutils
+from tempest.common import compute
 from tempest import config
 from tempest.lib.common.utils import data_utils
 from tempest.lib import decorators
@@ -1319,3 +1321,47 @@
     def test_create_node_with_description(self):
         _, body = self.create_node(self.chassis['uuid'], description='meow')
         self.assertEqual('meow', body['description'])
+
+
+class TestNodeGraphicalConsole(base.BaseBaremetalTest,
+                               compute.NoVNCValidateMixin):
+    """Tests for fake-graphical console connection."""
+
+    min_microversion = '1.96'
+
+    @classmethod
+    def skip_checks(cls):
+        super(TestNodeGraphicalConsole, cls).skip_checks()
+        if CONF.baremetal.driver != 'fake-hardware':
+            raise cls.skipException('These tests rely on fake-hardware')
+        if 'fake-graphical' not in CONF.baremetal.enabled_console_interfaces:
+            raise cls.skipException('These tests rely on fake-graphical being'
+                                    'included in enabled_console_interfaces')
+
+    def setUp(self):
+        super(TestNodeGraphicalConsole, self).setUp()
+
+        _, self.chassis = self.create_chassis()
+        _, self.node = self.create_node(self.chassis['uuid'],
+                                        console_interface="fake-graphical")
+
+    @decorators.idempotent_id('80504575-9b21-4670-92d1-143b948f9437')
+    def test_graphical_console_connection(self):
+        self.client.set_console_mode(self.node['uuid'], True)
+        waiters.wait_for_bm_node_status(self.client, self.node['uuid'],
+                                        'console_enabled', True)
+        self.addCleanup(self.client.set_console_mode, self.node['uuid'],
+                        False)
+
+        _, body = self.client.get_console(self.node['uuid'])
+        console_info = body['console_info']
+        self.assertEqual('vnc', console_info['type'])
+        # Do the initial HTTP Request to novncproxy to get the NoVNC JavaScript
+        self.validate_novnc_html(console_info['url'])
+        # Do the WebSockify HTTP Request to novncproxy to do the RFB connection
+        self.websocket = compute.create_websocket(console_info['url'])
+        self.addCleanup(self.websocket.close)
+        # Validate that we successfully connected and upgraded to Web Sockets
+        self.validate_websocket_upgrade()
+        # Validate the RFB Negotiation to determine if a valid VNC session
+        self.validate_rfb_negotiation()