# 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.

from cinderclient import api_versions
from osc_lib.cli import format_columns
from osc_lib import exceptions

from openstackclient.tests.unit.compute.v2 import fakes as compute_fakes
from openstackclient.tests.unit.identity.v3 import fakes as identity_fakes
from openstackclient.tests.unit.volume.v3 import fakes as volume_fakes
from openstackclient.volume.v3 import volume_attachment


class TestVolumeAttachment(volume_fakes.TestVolume):
    def setUp(self):
        super().setUp()

        self.volumes_mock = self.app.client_manager.volume.volumes
        self.volumes_mock.reset_mock()

        self.volume_attachments_mock = (
            self.app.client_manager.volume.attachments
        )
        self.volume_attachments_mock.reset_mock()

        self.projects_mock = self.app.client_manager.identity.projects
        self.projects_mock.reset_mock()

        self.servers_mock = self.app.client_manager.compute.servers
        self.servers_mock.reset_mock()


class TestVolumeAttachmentCreate(TestVolumeAttachment):
    volume = volume_fakes.create_one_volume()
    server = compute_fakes.create_one_server()
    volume_attachment = volume_fakes.create_one_volume_attachment(
        attrs={'instance': server.id, 'volume_id': volume.id},
    )

    columns = (
        'ID',
        'Volume ID',
        'Instance ID',
        'Status',
        'Attach Mode',
        'Attached At',
        'Detached At',
        'Properties',
    )
    data = (
        volume_attachment.id,
        volume_attachment.volume_id,
        volume_attachment.instance,
        volume_attachment.status,
        volume_attachment.attach_mode,
        volume_attachment.attached_at,
        volume_attachment.detached_at,
        format_columns.DictColumn(volume_attachment.connection_info),
    )

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

        self.volumes_mock.get.return_value = self.volume
        self.servers_mock.get.return_value = self.server
        # VolumeAttachmentManager.create returns a dict
        self.volume_attachments_mock.create.return_value = (
            self.volume_attachment.to_dict()
        )

        self.cmd = volume_attachment.CreateVolumeAttachment(self.app, None)

    def test_volume_attachment_create(self):
        self.app.client_manager.volume.api_version = api_versions.APIVersion(
            '3.27'
        )

        arglist = [
            self.volume.id,
            self.server.id,
        ]
        verifylist = [
            ('volume', self.volume.id),
            ('server', self.server.id),
            ('connect', False),
            ('initiator', None),
            ('ip', None),
            ('host', None),
            ('platform', None),
            ('os_type', None),
            ('multipath', False),
            ('mountpoint', None),
            ('mode', None),
        ]
        parsed_args = self.check_parser(self.cmd, arglist, verifylist)

        columns, data = self.cmd.take_action(parsed_args)

        self.volumes_mock.get.assert_called_once_with(self.volume.id)
        self.servers_mock.get.assert_called_once_with(self.server.id)
        self.volume_attachments_mock.create.assert_called_once_with(
            self.volume.id,
            {},
            self.server.id,
            None,
        )
        self.assertEqual(self.columns, columns)
        self.assertCountEqual(self.data, data)

    def test_volume_attachment_create_with_connect(self):
        self.app.client_manager.volume.api_version = api_versions.APIVersion(
            '3.54'
        )

        arglist = [
            self.volume.id,
            self.server.id,
            '--connect',
            '--initiator',
            'iqn.1993-08.org.debian:01:cad181614cec',
            '--ip',
            '192.168.1.20',
            '--host',
            'my-host',
            '--platform',
            'x86_64',
            '--os-type',
            'linux2',
            '--multipath',
            '--mountpoint',
            '/dev/vdb',
            '--mode',
            'null',
        ]
        verifylist = [
            ('volume', self.volume.id),
            ('server', self.server.id),
            ('connect', True),
            ('initiator', 'iqn.1993-08.org.debian:01:cad181614cec'),
            ('ip', '192.168.1.20'),
            ('host', 'my-host'),
            ('platform', 'x86_64'),
            ('os_type', 'linux2'),
            ('multipath', True),
            ('mountpoint', '/dev/vdb'),
            ('mode', 'null'),
        ]
        parsed_args = self.check_parser(self.cmd, arglist, verifylist)

        columns, data = self.cmd.take_action(parsed_args)

        connect_info = dict(
            [
                ('initiator', 'iqn.1993-08.org.debian:01:cad181614cec'),
                ('ip', '192.168.1.20'),
                ('host', 'my-host'),
                ('platform', 'x86_64'),
                ('os_type', 'linux2'),
                ('multipath', True),
                ('mountpoint', '/dev/vdb'),
            ]
        )

        self.volumes_mock.get.assert_called_once_with(self.volume.id)
        self.servers_mock.get.assert_called_once_with(self.server.id)
        self.volume_attachments_mock.create.assert_called_once_with(
            self.volume.id,
            connect_info,
            self.server.id,
            'null',
        )
        self.assertEqual(self.columns, columns)
        self.assertCountEqual(self.data, data)

    def test_volume_attachment_create_pre_v327(self):
        self.app.client_manager.volume.api_version = api_versions.APIVersion(
            '3.26'
        )

        arglist = [
            self.volume.id,
            self.server.id,
        ]
        verifylist = [
            ('volume', self.volume.id),
            ('server', self.server.id),
        ]
        parsed_args = self.check_parser(self.cmd, arglist, verifylist)

        exc = self.assertRaises(
            exceptions.CommandError, self.cmd.take_action, parsed_args
        )
        self.assertIn(
            '--os-volume-api-version 3.27 or greater is required', str(exc)
        )

    def test_volume_attachment_create_with_mode_pre_v354(self):
        self.app.client_manager.volume.api_version = api_versions.APIVersion(
            '3.53'
        )

        arglist = [
            self.volume.id,
            self.server.id,
            '--mode',
            'rw',
        ]
        verifylist = [
            ('volume', self.volume.id),
            ('server', self.server.id),
            ('mode', 'rw'),
        ]
        parsed_args = self.check_parser(self.cmd, arglist, verifylist)

        exc = self.assertRaises(
            exceptions.CommandError, self.cmd.take_action, parsed_args
        )
        self.assertIn(
            '--os-volume-api-version 3.54 or greater is required', str(exc)
        )

    def test_volume_attachment_create_with_connect_missing_arg(self):
        self.app.client_manager.volume.api_version = api_versions.APIVersion(
            '3.54'
        )

        arglist = [
            self.volume.id,
            self.server.id,
            '--initiator',
            'iqn.1993-08.org.debian:01:cad181614cec',
        ]
        verifylist = [
            ('volume', self.volume.id),
            ('server', self.server.id),
            ('connect', False),
            ('initiator', 'iqn.1993-08.org.debian:01:cad181614cec'),
        ]
        parsed_args = self.check_parser(self.cmd, arglist, verifylist)

        exc = self.assertRaises(
            exceptions.CommandError, self.cmd.take_action, parsed_args
        )
        self.assertIn(
            'You must specify the --connect option for any', str(exc)
        )


class TestVolumeAttachmentDelete(TestVolumeAttachment):
    volume_attachment = volume_fakes.create_one_volume_attachment()

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

        self.volume_attachments_mock.delete.return_value = None

        self.cmd = volume_attachment.DeleteVolumeAttachment(self.app, None)

    def test_volume_attachment_delete(self):
        self.app.client_manager.volume.api_version = api_versions.APIVersion(
            '3.27'
        )

        arglist = [
            self.volume_attachment.id,
        ]
        verifylist = [
            ('attachment', self.volume_attachment.id),
        ]
        parsed_args = self.check_parser(self.cmd, arglist, verifylist)

        result = self.cmd.take_action(parsed_args)

        self.volume_attachments_mock.delete.assert_called_once_with(
            self.volume_attachment.id,
        )
        self.assertIsNone(result)

    def test_volume_attachment_delete_pre_v327(self):
        self.app.client_manager.volume.api_version = api_versions.APIVersion(
            '3.26'
        )

        arglist = [
            self.volume_attachment.id,
        ]
        verifylist = [
            ('attachment', self.volume_attachment.id),
        ]
        parsed_args = self.check_parser(self.cmd, arglist, verifylist)

        exc = self.assertRaises(
            exceptions.CommandError, self.cmd.take_action, parsed_args
        )
        self.assertIn(
            '--os-volume-api-version 3.27 or greater is required', str(exc)
        )


class TestVolumeAttachmentSet(TestVolumeAttachment):
    volume_attachment = volume_fakes.create_one_volume_attachment()

    columns = (
        'ID',
        'Volume ID',
        'Instance ID',
        'Status',
        'Attach Mode',
        'Attached At',
        'Detached At',
        'Properties',
    )
    data = (
        volume_attachment.id,
        volume_attachment.volume_id,
        volume_attachment.instance,
        volume_attachment.status,
        volume_attachment.attach_mode,
        volume_attachment.attached_at,
        volume_attachment.detached_at,
        format_columns.DictColumn(volume_attachment.connection_info),
    )

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

        self.volume_attachments_mock.update.return_value = (
            self.volume_attachment
        )

        self.cmd = volume_attachment.SetVolumeAttachment(self.app, None)

    def test_volume_attachment_set(self):
        self.app.client_manager.volume.api_version = api_versions.APIVersion(
            '3.27'
        )

        arglist = [
            self.volume_attachment.id,
            '--initiator',
            'iqn.1993-08.org.debian:01:cad181614cec',
            '--ip',
            '192.168.1.20',
            '--host',
            'my-host',
            '--platform',
            'x86_64',
            '--os-type',
            'linux2',
            '--multipath',
            '--mountpoint',
            '/dev/vdb',
        ]
        verifylist = [
            ('attachment', self.volume_attachment.id),
            ('initiator', 'iqn.1993-08.org.debian:01:cad181614cec'),
            ('ip', '192.168.1.20'),
            ('host', 'my-host'),
            ('platform', 'x86_64'),
            ('os_type', 'linux2'),
            ('multipath', True),
            ('mountpoint', '/dev/vdb'),
        ]
        parsed_args = self.check_parser(self.cmd, arglist, verifylist)

        columns, data = self.cmd.take_action(parsed_args)

        connect_info = dict(
            [
                ('initiator', 'iqn.1993-08.org.debian:01:cad181614cec'),
                ('ip', '192.168.1.20'),
                ('host', 'my-host'),
                ('platform', 'x86_64'),
                ('os_type', 'linux2'),
                ('multipath', True),
                ('mountpoint', '/dev/vdb'),
            ]
        )

        self.volume_attachments_mock.update.assert_called_once_with(
            self.volume_attachment.id,
            connect_info,
        )
        self.assertEqual(self.columns, columns)
        self.assertCountEqual(self.data, data)

    def test_volume_attachment_set_pre_v327(self):
        self.app.client_manager.volume.api_version = api_versions.APIVersion(
            '3.26'
        )

        arglist = [
            self.volume_attachment.id,
            '--initiator',
            'iqn.1993-08.org.debian:01:cad181614cec',
        ]
        verifylist = [
            ('attachment', self.volume_attachment.id),
            ('initiator', 'iqn.1993-08.org.debian:01:cad181614cec'),
        ]
        parsed_args = self.check_parser(self.cmd, arglist, verifylist)

        exc = self.assertRaises(
            exceptions.CommandError, self.cmd.take_action, parsed_args
        )
        self.assertIn(
            '--os-volume-api-version 3.27 or greater is required', str(exc)
        )


class TestVolumeAttachmentComplete(TestVolumeAttachment):
    volume_attachment = volume_fakes.create_one_volume_attachment()

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

        self.volume_attachments_mock.complete.return_value = None

        self.cmd = volume_attachment.CompleteVolumeAttachment(self.app, None)

    def test_volume_attachment_complete(self):
        self.app.client_manager.volume.api_version = api_versions.APIVersion(
            '3.44'
        )

        arglist = [
            self.volume_attachment.id,
        ]
        verifylist = [
            ('attachment', self.volume_attachment.id),
        ]
        parsed_args = self.check_parser(self.cmd, arglist, verifylist)

        result = self.cmd.take_action(parsed_args)

        self.volume_attachments_mock.complete.assert_called_once_with(
            self.volume_attachment.id,
        )
        self.assertIsNone(result)

    def test_volume_attachment_complete_pre_v344(self):
        self.app.client_manager.volume.api_version = api_versions.APIVersion(
            '3.43'
        )

        arglist = [
            self.volume_attachment.id,
        ]
        verifylist = [
            ('attachment', self.volume_attachment.id),
        ]
        parsed_args = self.check_parser(self.cmd, arglist, verifylist)

        exc = self.assertRaises(
            exceptions.CommandError, self.cmd.take_action, parsed_args
        )
        self.assertIn(
            '--os-volume-api-version 3.44 or greater is required', str(exc)
        )


class TestVolumeAttachmentList(TestVolumeAttachment):
    project = identity_fakes.FakeProject.create_one_project()
    volume_attachments = volume_fakes.create_volume_attachments()

    columns = (
        'ID',
        'Volume ID',
        'Server ID',
        'Status',
    )
    data = [
        (
            volume_attachment.id,
            volume_attachment.volume_id,
            volume_attachment.instance,
            volume_attachment.status,
        )
        for volume_attachment in volume_attachments
    ]

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

        self.projects_mock.get.return_value = self.project
        self.volume_attachments_mock.list.return_value = (
            self.volume_attachments
        )

        self.cmd = volume_attachment.ListVolumeAttachment(self.app, None)

    def test_volume_attachment_list(self):
        self.app.client_manager.volume.api_version = api_versions.APIVersion(
            '3.27'
        )

        arglist = []
        verifylist = [
            ('project', None),
            ('all_projects', False),
            ('volume_id', None),
            ('status', None),
            ('marker', None),
            ('limit', None),
        ]
        parsed_args = self.check_parser(self.cmd, arglist, verifylist)

        columns, data = self.cmd.take_action(parsed_args)

        self.volume_attachments_mock.list.assert_called_once_with(
            search_opts={
                'all_tenants': False,
                'project_id': None,
                'status': None,
                'volume_id': None,
            },
            marker=None,
            limit=None,
        )
        self.assertEqual(self.columns, columns)
        self.assertCountEqual(tuple(self.data), data)

    def test_volume_attachment_list_with_options(self):
        self.app.client_manager.volume.api_version = api_versions.APIVersion(
            '3.27'
        )

        arglist = [
            '--project',
            self.project.name,
            '--volume-id',
            'volume-id',
            '--status',
            'attached',
            '--marker',
            'volume-attachment-id',
            '--limit',
            '2',
        ]
        verifylist = [
            ('project', self.project.name),
            ('all_projects', False),
            ('volume_id', 'volume-id'),
            ('status', 'attached'),
            ('marker', 'volume-attachment-id'),
            ('limit', 2),
        ]
        parsed_args = self.check_parser(self.cmd, arglist, verifylist)

        columns, data = self.cmd.take_action(parsed_args)

        self.volume_attachments_mock.list.assert_called_once_with(
            search_opts={
                'all_tenants': True,
                'project_id': self.project.id,
                'status': 'attached',
                'volume_id': 'volume-id',
            },
            marker='volume-attachment-id',
            limit=2,
        )
        self.assertEqual(self.columns, columns)
        self.assertCountEqual(tuple(self.data), data)

    def test_volume_attachment_list_pre_v327(self):
        self.app.client_manager.volume.api_version = api_versions.APIVersion(
            '3.26'
        )

        arglist = []
        verifylist = [
            ('project', None),
            ('all_projects', False),
            ('volume_id', None),
            ('status', None),
            ('marker', None),
            ('limit', None),
        ]
        parsed_args = self.check_parser(self.cmd, arglist, verifylist)

        exc = self.assertRaises(
            exceptions.CommandError, self.cmd.take_action, parsed_args
        )
        self.assertIn(
            '--os-volume-api-version 3.27 or greater is required', str(exc)
        )
