# Copyright 2020, Red Hat, Inc. All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
#      http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.

import datetime

from oslo_utils.fixture import uuidsentinel as uuids

from nova.tests.functional.api import client
from nova.tests.functional import integrated_helpers


class TestNonBootableImageMeta(integrated_helpers._IntegratedTestBase):
    """Regression test for bug 1895696

    This regression test asserts the behaviour of server creation requests when
    using an image with nonbootable properties either directly in the request
    or to create a volume that is then booted from.
    """

    microversion = 'latest'

    def setUp(self):
        super().setUp()

        # Add an image to the Glance fixture with cinder_encryption_key set
        timestamp = datetime.datetime(
            2011, 1, 1, 1, 2, 3, tzinfo=datetime.timezone.utc
        )
        cinder_encrypted_image = {
            'id': uuids.cinder_encrypted_image_uuid,
            'name': 'cinder_encryption_key_image',
            'created_at': timestamp,
            'updated_at': timestamp,
            'deleted_at': None,
            'deleted': False,
            'status': 'active',
            'is_public': False,
            'container_format': 'ova',
            'disk_format': 'vhd',
            'size': 74185822,
            'min_ram': 0,
            'min_disk': 0,
            'protected': False,
            'visibility': 'public',
            'tags': [],
            'properties': {
                'cinder_encryption_key_id': uuids.cinder_encryption_key_id,
            }
        }
        self.glance.create(None, cinder_encrypted_image)

        # Mock out nova.volume.cinder.API.{create,get} so that when n-api
        # requests that c-api create a volume from the above image that the
        # response includes cinder_encryption_key_id in the
        # volume_image_metadata
        cinder_encrypted_volume = {
            'status': 'available',
            'display_name': 'cinder_encrypted_volume',
            'attach_status': 'detached',
            'id': uuids.cinder_encrypted_volume_uuid,
            'multiattach': False,
            'size': 1,
            'encrypted': True,
            'volume_image_metadata': {
                'cinder_encryption_key_id': uuids.cinder_encryption_key_id
            }
        }

        def fake_cinder_create(self_api, context, size, name, description,
                snapshot=None, image_id=None, volume_type=None, metadata=None,
                availability_zone=None):
            if image_id == uuids.cinder_encrypted_image_uuid:
                return cinder_encrypted_volume
        self.stub_out(
            'nova.volume.cinder.API.create', fake_cinder_create)

        def fake_cinder_get(self_api, context, volume_id, microversion=None):
            return cinder_encrypted_volume
        self.stub_out(
            'nova.volume.cinder.API.get', fake_cinder_get)

    def test_nonbootable_metadata_image_metadata(self):
        """Assert behaviour when booting from an encrypted image
        """
        server = self._build_server(
            name='test_nonbootable_metadata_bfv_image_metadata',
            image_uuid=uuids.cinder_encrypted_image_uuid,
            networks='none'
        )
        # NOTE(lyarwood): This should always fail as Nova will attempt to boot
        # directly from this encrypted image.
        ex = self.assertRaises(
            client.OpenStackApiException, self.api.post_server,
            {'server': server})
        self.assertEqual(400, ex.response.status_code)
        self.assertIn(
            "Direct booting of an image uploaded from an encrypted volume is "
            "unsupported", str(ex))

    def test_nonbootable_metadata_bfv_image_metadata(self):
        """Assert behaviour when n-api creates volume using an encrypted image
        """
        server = self._build_server(
            name='test_nonbootable_metadata_bfv_image_metadata',
            image_uuid='', networks='none'
        )
        # TODO(lyarwood): Merge this into _build_server
        server['block_device_mapping_v2'] = [{
            'source_type': 'image',
            'destination_type': 'volume',
            'boot_index': 0,
            'uuid': uuids.cinder_encrypted_image_uuid,
            'volume_size': 1,
        }]

        # Assert that this request is accepted and the server moves to ACTIVE
        server = self.api.post_server({'server': server})
        self._wait_for_state_change(server, 'ACTIVE')

    def test_nonbootable_metadata_bfv_volume_image_metadata(self):
        """Assert behaviour when c-api has created volume using encrypted image
        """
        server = self._build_server(
            name='test_nonbootable_metadata_bfv_volume_image_metadata',
            image_uuid='', networks='none'
        )
        # TODO(lyarwood): Merge this into _build_server
        server['block_device_mapping_v2'] = [{
            'source_type': 'volume',
            'destination_type': 'volume',
            'boot_index': 0,
            'uuid': uuids.cinder_encrypted_volume_uuid,
        }]

        # Assert that this request is accepted and the server moves to ACTIVE
        server = self.api.post_server({'server': server})
        self._wait_for_state_change(server, 'ACTIVE')
