diff --git a/api-ref/source/parameters.yaml b/api-ref/source/parameters.yaml index b61a870a9a..8f0650a243 100644 --- a/api-ref/source/parameters.yaml +++ b/api-ref/source/parameters.yaml @@ -2390,7 +2390,7 @@ required_extra_specs: description: | The required extra specifications for the share type: ``driver_handles_share_servers``. ``snapshot_support`` was - treated as a required extra-specification until + treated as a required extra-specification until api version 2.24. in: body required: true diff --git a/api-ref/source/samples/share-network-add-security-service-response-with-subnets.json b/api-ref/source/samples/share-network-add-security-service-response-with-subnets.json index 76456bc5e5..c99188b05b 100644 --- a/api-ref/source/samples/share-network-add-security-service-response-with-subnets.json +++ b/api-ref/source/samples/share-network-add-security-service-response-with-subnets.json @@ -1,26 +1,26 @@ -{ - "share_network": { - "name": "net2", - "created_at": "2019-11-10T12:31:12.000000", - "updated_at": null, - "id": "d8ae6799-2567-4a89-aafb-fa4424350d2b", - "project_id": "16e1ab15c35a457e9c2b2aa189f544e1", - "description": null, - "share_network_subnets": [ - { - "id": "e4db03dc-6041-4c6a-a8f9-80bb4141a1eb", - "availability_zone": null, - "created_at": "2019-11-10T12:31:12.000000", - "updated_at": "2019-11-10T12:31:12.000000", - "segmentation_id": null, - "neutron_net_id": "62187648-6617-4509-a780-ffc973a7fe43", - "neutron_subnet_id": "2276888a-27c1-47c2-82a0-ea33050128b5", - "ip_version": 4, - "cidr": "172.24.5.0/24", - "network_type": "flat", - "mtu": 1500, - "gateway": "172.24.5.1" - } - ] - } -} \ No newline at end of file +{ + "share_network": { + "name": "net2", + "created_at": "2019-11-10T12:31:12.000000", + "updated_at": null, + "id": "d8ae6799-2567-4a89-aafb-fa4424350d2b", + "project_id": "16e1ab15c35a457e9c2b2aa189f544e1", + "description": null, + "share_network_subnets": [ + { + "id": "e4db03dc-6041-4c6a-a8f9-80bb4141a1eb", + "availability_zone": null, + "created_at": "2019-11-10T12:31:12.000000", + "updated_at": "2019-11-10T12:31:12.000000", + "segmentation_id": null, + "neutron_net_id": "62187648-6617-4509-a780-ffc973a7fe43", + "neutron_subnet_id": "2276888a-27c1-47c2-82a0-ea33050128b5", + "ip_version": 4, + "cidr": "172.24.5.0/24", + "network_type": "flat", + "mtu": 1500, + "gateway": "172.24.5.1" + } + ] + } +} diff --git a/api-ref/source/samples/share-network-create-response-with-subnets.json b/api-ref/source/samples/share-network-create-response-with-subnets.json index ee3251bd0c..5756ef2bd2 100644 --- a/api-ref/source/samples/share-network-create-response-with-subnets.json +++ b/api-ref/source/samples/share-network-create-response-with-subnets.json @@ -1,28 +1,28 @@ -{ - "share_network": { - "name": "my_network", - "created_at": "2019-09-07T14:37:00.583656", - "updated_at": null, - "id": "77eb3421-4549-4789-ac39-0d5185d68c29", - "project_id": "e10a683c20da41248cfd5e1ab3d88c62", - "description": "This is my share network", - "security_service_update_support": true, - "status": "active", - "share_network_subnets": [ - { - "id": "91cc63b5-6c61-4078-b054-560923709654", - "availability_zone": "manila-zone-0", - "created_at": "2019-10-04T20:49:11.000000", - "updated_at": null, - "segmentation_id": null, - "neutron_net_id": "998b42ee-2cee-4d36-8b95-67b5ca1f2109", - "neutron_subnet_id": "53482b62-2c84-4a53-b6ab-30d9d9800d06", - "ip_version": null, - "cidr": null, - "network_type": null, - "mtu": null, - "gateway": null - } - ] - } -} +{ + "share_network": { + "name": "my_network", + "created_at": "2019-09-07T14:37:00.583656", + "updated_at": null, + "id": "77eb3421-4549-4789-ac39-0d5185d68c29", + "project_id": "e10a683c20da41248cfd5e1ab3d88c62", + "description": "This is my share network", + "security_service_update_support": true, + "status": "active", + "share_network_subnets": [ + { + "id": "91cc63b5-6c61-4078-b054-560923709654", + "availability_zone": "manila-zone-0", + "created_at": "2019-10-04T20:49:11.000000", + "updated_at": null, + "segmentation_id": null, + "neutron_net_id": "998b42ee-2cee-4d36-8b95-67b5ca1f2109", + "neutron_subnet_id": "53482b62-2c84-4a53-b6ab-30d9d9800d06", + "ip_version": null, + "cidr": null, + "network_type": null, + "mtu": null, + "gateway": null + } + ] + } +} diff --git a/api-ref/source/samples/share-network-remove-security-service-response-with-subnets.json b/api-ref/source/samples/share-network-remove-security-service-response-with-subnets.json index 77110ea035..bbf0a969ad 100644 --- a/api-ref/source/samples/share-network-remove-security-service-response-with-subnets.json +++ b/api-ref/source/samples/share-network-remove-security-service-response-with-subnets.json @@ -1,28 +1,28 @@ -{ - "share_network": { - "name": "net2", - "created_at": "2019-11-07T12:31:12.000000", - "updated_at": null, - "id": "d8ae6799-2567-4a89-aafb-fa4424350d2b", - "project_id": "16e1ab15c35a457e9c2b2aa189f544e1", - "description": null, - "security_service_update_support": true, - "status": "active", - "share_network_subnets": [ - { - "id": "e4db03dc-6041-4c6a-a8f9-80bb4141a1eb", - "availability_zone": null, - "created_at": "2019-11-07T12:31:12.000000", - "updated_at": "2019-12-12T12:31:12.000000", - "segmentation_id": null, - "neutron_net_id": "62187648-6617-4509-a780-ffc973a7fe43", - "neutron_subnet_id": "2276888a-27c1-47c2-82a0-ea33050128b5", - "ip_version": 4, - "cidr": "172.24.5.0/24", - "network_type": "flat", - "mtu": 1500, - "gateway": "172.24.5.1" - } - ] - } -} \ No newline at end of file +{ + "share_network": { + "name": "net2", + "created_at": "2019-11-07T12:31:12.000000", + "updated_at": null, + "id": "d8ae6799-2567-4a89-aafb-fa4424350d2b", + "project_id": "16e1ab15c35a457e9c2b2aa189f544e1", + "description": null, + "security_service_update_support": true, + "status": "active", + "share_network_subnets": [ + { + "id": "e4db03dc-6041-4c6a-a8f9-80bb4141a1eb", + "availability_zone": null, + "created_at": "2019-11-07T12:31:12.000000", + "updated_at": "2019-12-12T12:31:12.000000", + "segmentation_id": null, + "neutron_net_id": "62187648-6617-4509-a780-ffc973a7fe43", + "neutron_subnet_id": "2276888a-27c1-47c2-82a0-ea33050128b5", + "ip_version": 4, + "cidr": "172.24.5.0/24", + "network_type": "flat", + "mtu": 1500, + "gateway": "172.24.5.1" + } + ] + } +} diff --git a/api-ref/source/samples/share-network-show-response-with-subnets.json b/api-ref/source/samples/share-network-show-response-with-subnets.json index 494e321280..365ba1d45e 100644 --- a/api-ref/source/samples/share-network-show-response-with-subnets.json +++ b/api-ref/source/samples/share-network-show-response-with-subnets.json @@ -1,27 +1,27 @@ -{ - "share_network": { - "id": "1324e7d3-fba8-45e4-bb37-b59c12eb06dc", - "name": "net_my1", - "project_id": "16e1ab15c35a457e9c2b2aa189f544e1", - "created_at": "2019-10-02T17:49:43.000000", - "description": null, - "security_service_update_support": true, - "status": "active", - "share_network_subnets": [ - { - "id": "e4db03dc-6041-4c6a-a8f9-80bb4141a1eb", - "availability_zone": null, - "created_at": "2019-10-02T17:49:43.000000", - "updated_at": "2019-10-03T12:17:39.000000", - "segmentation_id": null, - "neutron_net_id": "62187648-6617-4509-a780-ffc973a7fe43", - "neutron_subnet_id": "2276888a-27c1-47c2-82a0-ea33050128b5", - "ip_version": 4, - "cidr": "172.24.5.0/24", - "network_type": "flat", - "mtu": 1500, - "gateway": "172.24.5.1" - } - ] - } -} \ No newline at end of file +{ + "share_network": { + "id": "1324e7d3-fba8-45e4-bb37-b59c12eb06dc", + "name": "net_my1", + "project_id": "16e1ab15c35a457e9c2b2aa189f544e1", + "created_at": "2019-10-02T17:49:43.000000", + "description": null, + "security_service_update_support": true, + "status": "active", + "share_network_subnets": [ + { + "id": "e4db03dc-6041-4c6a-a8f9-80bb4141a1eb", + "availability_zone": null, + "created_at": "2019-10-02T17:49:43.000000", + "updated_at": "2019-10-03T12:17:39.000000", + "segmentation_id": null, + "neutron_net_id": "62187648-6617-4509-a780-ffc973a7fe43", + "neutron_subnet_id": "2276888a-27c1-47c2-82a0-ea33050128b5", + "ip_version": 4, + "cidr": "172.24.5.0/24", + "network_type": "flat", + "mtu": 1500, + "gateway": "172.24.5.1" + } + ] + } +} diff --git a/api-ref/source/samples/share-network-subnet-create-request.json b/api-ref/source/samples/share-network-subnet-create-request.json index 894e0162fa..6c95b6c38c 100644 --- a/api-ref/source/samples/share-network-subnet-create-request.json +++ b/api-ref/source/samples/share-network-subnet-create-request.json @@ -1,7 +1,7 @@ -{ - "share-network-subnet": { - "neutron_net_id": "62187648-6617-4509-a780-ffc973a7fe43", - "neutron_subnet_id": "2276888a-27c1-47c2-82a0-ea33050128b5", - "availability_zone": "manila-zone-0" - } -} +{ + "share-network-subnet": { + "neutron_net_id": "62187648-6617-4509-a780-ffc973a7fe43", + "neutron_subnet_id": "2276888a-27c1-47c2-82a0-ea33050128b5", + "availability_zone": "manila-zone-0" + } +} diff --git a/api-ref/source/samples/share-network-subnet-create-response.json b/api-ref/source/samples/share-network-subnet-create-response.json index 09ec00b86c..b4aa363494 100644 --- a/api-ref/source/samples/share-network-subnet-create-response.json +++ b/api-ref/source/samples/share-network-subnet-create-response.json @@ -1,18 +1,18 @@ -{ - "share_network_subnet": { - "id": "8ebe964d-ac48-4e43-93ed-b1768148f8f4", - "availability_zone": "manila-zone-0", - "share_network_id": "1324e7d3-fba8-45e4-bb37-b59c12eb06dc", - "share_network_name": "net_my1", - "created_at": "2019-10-03T02:25:12.000000", - "segmentation_id": null, - "neutron_subnet_id": "2276888a-27c1-47c2-82a0-ea33050128b5", - "updated_at": null, - "neutron_net_id": "62187648-6617-4509-a780-ffc973a7fe43", - "ip_version": null, - "cidr": null, - "network_type": null, - "mtu": null, - "gateway": null - } -} +{ + "share_network_subnet": { + "id": "8ebe964d-ac48-4e43-93ed-b1768148f8f4", + "availability_zone": "manila-zone-0", + "share_network_id": "1324e7d3-fba8-45e4-bb37-b59c12eb06dc", + "share_network_name": "net_my1", + "created_at": "2019-10-03T02:25:12.000000", + "segmentation_id": null, + "neutron_subnet_id": "2276888a-27c1-47c2-82a0-ea33050128b5", + "updated_at": null, + "neutron_net_id": "62187648-6617-4509-a780-ffc973a7fe43", + "ip_version": null, + "cidr": null, + "network_type": null, + "mtu": null, + "gateway": null + } +} diff --git a/api-ref/source/samples/share-network-subnet-list-response.json b/api-ref/source/samples/share-network-subnet-list-response.json index b3729ddf3a..fb59f82380 100644 --- a/api-ref/source/samples/share-network-subnet-list-response.json +++ b/api-ref/source/samples/share-network-subnet-list-response.json @@ -1,36 +1,36 @@ -{ - "share_network_subnets": [ - { - "id": "a7507a16-98bb-476c-ba90-487e4b4775fa", - "availability_zone": null, - "share_network_id": "8bc488d8-52f7-46cb-91b1-89dd92cae972", - "share_network_name": "sn_test", - "created_at": "2019-10-03T18:30:15.000000", - "segmentation_id": null, - "neutron_subnet_id": "dc0a37f0-81b0-4eb5-aad8-deffda5ff4ca", - "updated_at": null, - "neutron_net_id": "70bc8f03-525c-4334-a51b-261a024681c5", - "ip_version": 4, - "cidr": "10.190.5.0/24", - "network_type": "flat", - "mtu": 1500, - "gateway": "10.190.5.1" - }, - { - "id": "8ebe964d-ac48-4e43-93ed-b1768148f8f4", - "availability_zone": "manila-zone-0", - "share_network_id": "8bc488d8-52f7-46cb-91b1-89dd92cae972", - "share_network_name": "sn_test", - "created_at": "2019-10-02T01:35:10.000000", - "segmentation_id": null, - "neutron_subnet_id": "2276888a-27c1-47c2-82a0-ea33050128b5", - "updated_at": null, - "neutron_net_id": "62187648-6617-4509-a780-ffc973a7fe43", - "ip_version": 4, - "cidr": "172.24.5.0/24", - "network_type": "flat", - "mtu": 1500, - "gateway": "172.24.5.1" - } - ] -} +{ + "share_network_subnets": [ + { + "id": "a7507a16-98bb-476c-ba90-487e4b4775fa", + "availability_zone": null, + "share_network_id": "8bc488d8-52f7-46cb-91b1-89dd92cae972", + "share_network_name": "sn_test", + "created_at": "2019-10-03T18:30:15.000000", + "segmentation_id": null, + "neutron_subnet_id": "dc0a37f0-81b0-4eb5-aad8-deffda5ff4ca", + "updated_at": null, + "neutron_net_id": "70bc8f03-525c-4334-a51b-261a024681c5", + "ip_version": 4, + "cidr": "10.190.5.0/24", + "network_type": "flat", + "mtu": 1500, + "gateway": "10.190.5.1" + }, + { + "id": "8ebe964d-ac48-4e43-93ed-b1768148f8f4", + "availability_zone": "manila-zone-0", + "share_network_id": "8bc488d8-52f7-46cb-91b1-89dd92cae972", + "share_network_name": "sn_test", + "created_at": "2019-10-02T01:35:10.000000", + "segmentation_id": null, + "neutron_subnet_id": "2276888a-27c1-47c2-82a0-ea33050128b5", + "updated_at": null, + "neutron_net_id": "62187648-6617-4509-a780-ffc973a7fe43", + "ip_version": 4, + "cidr": "172.24.5.0/24", + "network_type": "flat", + "mtu": 1500, + "gateway": "172.24.5.1" + } + ] +} diff --git a/api-ref/source/samples/share-network-subnet-show-response.json b/api-ref/source/samples/share-network-subnet-show-response.json index c60041fdfd..b05539d13f 100644 --- a/api-ref/source/samples/share-network-subnet-show-response.json +++ b/api-ref/source/samples/share-network-subnet-show-response.json @@ -1,18 +1,18 @@ -{ - "share_network_subnet": { - "id": "e4db03dc-6041-4c6a-a8f9-80bb4141a1eb", - "availability_zone": null, - "share_network_id":"1324e7d3-fba8-45e4-bb37-b59c12eb06dc", - "share_network_name": "net_my1", - "created_at": "2019-10-01T17:49:43.000000", - "segmentation_id": null, - "neutron_subnet_id": "2276888a-27c1-47c2-82a0-ea33050128b5", - "updated_at": "2019-11-02T12:17:39.000000", - "neutron_net_id": "62187648-6617-4509-a780-ffc973a7fe43", - "ip_version": 4, - "cidr": "172.24.5.0/24", - "network_type": "flat", - "mtu": 1500, - "gateway": "172.24.5.1" - } -} +{ + "share_network_subnet": { + "id": "e4db03dc-6041-4c6a-a8f9-80bb4141a1eb", + "availability_zone": null, + "share_network_id":"1324e7d3-fba8-45e4-bb37-b59c12eb06dc", + "share_network_name": "net_my1", + "created_at": "2019-10-01T17:49:43.000000", + "segmentation_id": null, + "neutron_subnet_id": "2276888a-27c1-47c2-82a0-ea33050128b5", + "updated_at": "2019-11-02T12:17:39.000000", + "neutron_net_id": "62187648-6617-4509-a780-ffc973a7fe43", + "ip_version": 4, + "cidr": "172.24.5.0/24", + "network_type": "flat", + "mtu": 1500, + "gateway": "172.24.5.1" + } +} diff --git a/api-ref/source/samples/share-network-update-response-with-subnets.json b/api-ref/source/samples/share-network-update-response-with-subnets.json index a5a93824ac..a4fa10f7ad 100644 --- a/api-ref/source/samples/share-network-update-response-with-subnets.json +++ b/api-ref/source/samples/share-network-update-response-with-subnets.json @@ -1,28 +1,28 @@ -{ - "share_network":{ - "id": "2b33cd3a-3049-4f36-a2fd-f7a211eb9202", - "name": "update my network", - "project_id": "79ed3be75dbb4d03afd687b758fcc2c0", - "created_at": "2019-11-12T17:18:10.000000", - "updated_at": null, - "description": "i'm adding a description", - "security_service_update_support": true, - "status": "active", - "share_network_subnets": [ - { - "id": "687ab361-5c40-406e-945c-6326254782d4", - "availability_zone": null, - "created_at": "2019-11-13T17:18:10.000000", - "updated_at": "2019-11-13T17:18:56.000000", - "segmentation_id": null, - "neutron_net_id": "998b42ee-2cee-4d36-8b95-67b5ca1f2109", - "neutron_subnet_id": "53482b62-2c84-4a53-b6ab-30d9d9800d06", - "ip_version": 4, - "cidr": "172.24.5.0/24", - "network_type": "flat", - "mtu": 1500, - "gateway": "172.24.5.1" - } - ] - } -} \ No newline at end of file +{ + "share_network":{ + "id": "2b33cd3a-3049-4f36-a2fd-f7a211eb9202", + "name": "update my network", + "project_id": "79ed3be75dbb4d03afd687b758fcc2c0", + "created_at": "2019-11-12T17:18:10.000000", + "updated_at": null, + "description": "i'm adding a description", + "security_service_update_support": true, + "status": "active", + "share_network_subnets": [ + { + "id": "687ab361-5c40-406e-945c-6326254782d4", + "availability_zone": null, + "created_at": "2019-11-13T17:18:10.000000", + "updated_at": "2019-11-13T17:18:56.000000", + "segmentation_id": null, + "neutron_net_id": "998b42ee-2cee-4d36-8b95-67b5ca1f2109", + "neutron_subnet_id": "53482b62-2c84-4a53-b6ab-30d9d9800d06", + "ip_version": 4, + "cidr": "172.24.5.0/24", + "network_type": "flat", + "mtu": 1500, + "gateway": "172.24.5.1" + } + ] + } +} diff --git a/api-ref/source/samples/share-networks-list-detailed-response-with-subnets.json b/api-ref/source/samples/share-networks-list-detailed-response-with-subnets.json index e3c9a7d402..2d03edcac1 100644 --- a/api-ref/source/samples/share-networks-list-detailed-response-with-subnets.json +++ b/api-ref/source/samples/share-networks-list-detailed-response-with-subnets.json @@ -1,66 +1,66 @@ -{ - "share_networks": [ - { - "id": "03987b5f-cb79-4f5f-a590-f6936b91b49e", - "name": "net_my1", - "project_id": "16e1ab15c35a457e9c2b2aa189f544e1", - "created_at": "2019-10-02T12:33:36.000000", - "updated_at": null, - "description": null, - "share_network_subnets": [ - { - "id": "022aa495-845e-42a6-9d83-a38f164053c9", - "availability_zone": null, - "created_at": "2019-10-02T12:33:36.000000", - "updated_at": null, - "segmentation_id": null, - "neutron_net_id": "f00732aa-7721-455d-ba14-ec37619ea13f", - "neutron_subnet_id": "eb7adcf8-ce71-43e3-b4c2-cf81da9f89a", - "ip_version": null, - "cidr": null, - "network_type": null, - "mtu": null, - "gateway": null - } - ] - }, - { - "id": "1324e7d3-fba8-45e4-bb37-b59c12eb06dc", - "name": "net_my2", - "project_id": "16e1ab15c35a457e9c2b2aa189f544e1", - "created_at": "2019-07-01T17:49:43.000000", - "updated_at": "2019-07-02T12:17:39.000000", - "description": null, - "share_network_subnets": [ - { - "id": "8ebe964d-ac48-4e43-93ed-b1768148f8f4", - "availability_zone": "manila-zone-0", - "created_at": "2019-10-03T02:25:12.000000", - "updated_at": null, - "segmentation_id": null, - "neutron_net_id": "62187648-6617-4509-a780-ffc973a7fe43", - "neutron_subnet_id": "2276888a-27c1-47c2-82a0-ea3050128b5", - "ip_version": null, - "cidr": null, - "network_type": null, - "mtu": null, - "gateway": null - }, - { - "id": "e4db03dc-6041-4c6a-a8f9-80bb4141a1eb", - "availability_zone": null, - "created_at": "2019-07-01T17:49:43.000000", - "updated_at": "2019-07-02T12:17:39.000000", - "segmentation_id": null, - "neutron_net_id": "62187648-6617-4509-a780-ffc973a7fe43", - "neutron_subnet_id": "2276888a-27c1-47c2-820-ea33050128b5", - "ip_version": 4, - "cidr": "172.24.5.0/24", - "network_type": "flat", - "mtu": 1500, - "gateway": "172.24.5.1" - } - ] - } - ] -} \ No newline at end of file +{ + "share_networks": [ + { + "id": "03987b5f-cb79-4f5f-a590-f6936b91b49e", + "name": "net_my1", + "project_id": "16e1ab15c35a457e9c2b2aa189f544e1", + "created_at": "2019-10-02T12:33:36.000000", + "updated_at": null, + "description": null, + "share_network_subnets": [ + { + "id": "022aa495-845e-42a6-9d83-a38f164053c9", + "availability_zone": null, + "created_at": "2019-10-02T12:33:36.000000", + "updated_at": null, + "segmentation_id": null, + "neutron_net_id": "f00732aa-7721-455d-ba14-ec37619ea13f", + "neutron_subnet_id": "eb7adcf8-ce71-43e3-b4c2-cf81da9f89a", + "ip_version": null, + "cidr": null, + "network_type": null, + "mtu": null, + "gateway": null + } + ] + }, + { + "id": "1324e7d3-fba8-45e4-bb37-b59c12eb06dc", + "name": "net_my2", + "project_id": "16e1ab15c35a457e9c2b2aa189f544e1", + "created_at": "2019-07-01T17:49:43.000000", + "updated_at": "2019-07-02T12:17:39.000000", + "description": null, + "share_network_subnets": [ + { + "id": "8ebe964d-ac48-4e43-93ed-b1768148f8f4", + "availability_zone": "manila-zone-0", + "created_at": "2019-10-03T02:25:12.000000", + "updated_at": null, + "segmentation_id": null, + "neutron_net_id": "62187648-6617-4509-a780-ffc973a7fe43", + "neutron_subnet_id": "2276888a-27c1-47c2-82a0-ea3050128b5", + "ip_version": null, + "cidr": null, + "network_type": null, + "mtu": null, + "gateway": null + }, + { + "id": "e4db03dc-6041-4c6a-a8f9-80bb4141a1eb", + "availability_zone": null, + "created_at": "2019-07-01T17:49:43.000000", + "updated_at": "2019-07-02T12:17:39.000000", + "segmentation_id": null, + "neutron_net_id": "62187648-6617-4509-a780-ffc973a7fe43", + "neutron_subnet_id": "2276888a-27c1-47c2-820-ea33050128b5", + "ip_version": 4, + "cidr": "172.24.5.0/24", + "network_type": "flat", + "mtu": 1500, + "gateway": "172.24.5.1" + } + ] + } + ] +} diff --git a/api-ref/source/samples/share-server-migration-cancel-request.json b/api-ref/source/samples/share-server-migration-cancel-request.json index 9fa8c454ec..dad64c20a1 100644 --- a/api-ref/source/samples/share-server-migration-cancel-request.json +++ b/api-ref/source/samples/share-server-migration-cancel-request.json @@ -1,3 +1,3 @@ -{ - "migration_cancel":null -} \ No newline at end of file +{ + "migration_cancel":null +} diff --git a/api-ref/source/samples/share-server-migration-check-compatibility-request.json b/api-ref/source/samples/share-server-migration-check-compatibility-request.json index dac7860f77..07cea62b8c 100644 --- a/api-ref/source/samples/share-server-migration-check-compatibility-request.json +++ b/api-ref/source/samples/share-server-migration-check-compatibility-request.json @@ -1,9 +1,9 @@ -{ - "migration_check": { - "host": "foohost2@backend2", - "preserve_snapshots": "True", - "writable": "True", - "nondisruptive": "True", - "new_share_network_id": null - } -} \ No newline at end of file +{ + "migration_check": { + "host": "foohost2@backend2", + "preserve_snapshots": "True", + "writable": "True", + "nondisruptive": "True", + "new_share_network_id": null + } +} diff --git a/api-ref/source/samples/share-server-migration-check-compatibility-response.json b/api-ref/source/samples/share-server-migration-check-compatibility-response.json index 4a44b1b373..3bce445f26 100644 --- a/api-ref/source/samples/share-server-migration-check-compatibility-response.json +++ b/api-ref/source/samples/share-server-migration-check-compatibility-response.json @@ -1,18 +1,18 @@ -{ - "compatible": false, - "requested_capabilities": { - "writable": "True", - "nondisruptive": "True", - "preserve_snapshots": "True", - "share_network_id": null, - "host": "foohost2@backend2" - }, - "supported_capabilities": { - "writable": true, - "nondisruptive": false, - "preserve_snapshots": true, - "share_network_id": "1d04b755-649f-46a4-964c-be9f0395af13", - "migration_cancel": true, - "migration_get_progress": true - } -} \ No newline at end of file +{ + "compatible": false, + "requested_capabilities": { + "writable": "True", + "nondisruptive": "True", + "preserve_snapshots": "True", + "share_network_id": null, + "host": "foohost2@backend2" + }, + "supported_capabilities": { + "writable": true, + "nondisruptive": false, + "preserve_snapshots": true, + "share_network_id": "1d04b755-649f-46a4-964c-be9f0395af13", + "migration_cancel": true, + "migration_get_progress": true + } +} diff --git a/api-ref/source/samples/share-server-migration-complete-request.json b/api-ref/source/samples/share-server-migration-complete-request.json index 69ae47d65b..cbf0e8a8a4 100644 --- a/api-ref/source/samples/share-server-migration-complete-request.json +++ b/api-ref/source/samples/share-server-migration-complete-request.json @@ -1,3 +1,3 @@ -{ - "migration_complete": null -} \ No newline at end of file +{ + "migration_complete": null +} diff --git a/api-ref/source/samples/share-server-migration-complete-response.json b/api-ref/source/samples/share-server-migration-complete-response.json index 06b2ce44c6..9720596edf 100644 --- a/api-ref/source/samples/share-server-migration-complete-response.json +++ b/api-ref/source/samples/share-server-migration-complete-response.json @@ -1,3 +1,3 @@ -{ - "destination_share_server_id": "c2f71561-85e2-4ccb-a91a-e44f9ff6f7ef" -} \ No newline at end of file +{ + "destination_share_server_id": "c2f71561-85e2-4ccb-a91a-e44f9ff6f7ef" +} diff --git a/api-ref/source/samples/share-server-migration-get-progress-request.json b/api-ref/source/samples/share-server-migration-get-progress-request.json index f6bad77897..8ff6495776 100644 --- a/api-ref/source/samples/share-server-migration-get-progress-request.json +++ b/api-ref/source/samples/share-server-migration-get-progress-request.json @@ -1,3 +1,3 @@ -{ - "migration_get_progress": null -} \ No newline at end of file +{ + "migration_get_progress": null +} diff --git a/api-ref/source/samples/share-server-migration-get-progress-response.json b/api-ref/source/samples/share-server-migration-get-progress-response.json index 0c7cd65207..7b887cbb40 100644 --- a/api-ref/source/samples/share-server-migration-get-progress-response.json +++ b/api-ref/source/samples/share-server-migration-get-progress-response.json @@ -1,5 +1,5 @@ -{ - "total_progress": 50, - "task_state": "migration_driver_in_progress", - "destination_share_server_id": "c2f71561-85e2-4ccb-a91a-e44f9ff6f7ef" -} \ No newline at end of file +{ + "total_progress": 50, + "task_state": "migration_driver_in_progress", + "destination_share_server_id": "c2f71561-85e2-4ccb-a91a-e44f9ff6f7ef" +} diff --git a/api-ref/source/samples/share-server-migration-start-request.json b/api-ref/source/samples/share-server-migration-start-request.json index 7ad7db348b..bfdbfab099 100644 --- a/api-ref/source/samples/share-server-migration-start-request.json +++ b/api-ref/source/samples/share-server-migration-start-request.json @@ -1,9 +1,9 @@ -{ - "migration_start": { - "host": "foohost2@backend2", - "preserve_snapshots": "True", - "writable": "True", - "nondisruptive": "False", - "new_share_network_id": null - } -} \ No newline at end of file +{ + "migration_start": { + "host": "foohost2@backend2", + "preserve_snapshots": "True", + "writable": "True", + "nondisruptive": "False", + "new_share_network_id": null + } +} diff --git a/manila/share/drivers/macrosan/macrosan_constants.py b/manila/share/drivers/macrosan/macrosan_constants.py index dc0fb61ccf..5c2dfe8cb9 100644 --- a/manila/share/drivers/macrosan/macrosan_constants.py +++ b/manila/share/drivers/macrosan/macrosan_constants.py @@ -1,44 +1,44 @@ -# Copyright (c) 2022 MacroSAN Technologies Co., Ltd. -# 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. - -NFS_NON_CONFIG = 0 -NFS_ENABLED = 1 -NFS_DISABLED = 2 -NFS_EXCEPTION = 3 -NFS_NON_SUPPORTED = 0 -NFS_SUPPORTED = 1 - -CIFS_SHARE_MODE = '2' -CIFS_ENABLED = '1' -CIFS_NON_CONFIG = '-1' -CIFS_DISABLED = '-2' -CIFS_EXCEPTION = '-3' - -USER_NOT_EXIST = '0' -USER_EXIST = '1' -USER_FORMAT_ERROR = '2' - -GROUP_NOT_EXIST = '0' -GROUP_EXIST = '1' -GROUP_FORMAT_ERROR = '2' - -CODE_SUCCESS = 0 -CODE_NOT_FOUND = 4 -CODE_SOURCE_NOT_EXIST = 403 - -TOKEN_EXPIRED = 301 -TOKEN_VERIFY_FAILED = 302 -TOKEN_FORMAT_ERROR = 303 -TOKEN_REQUIRED = 304 +# Copyright (c) 2022 MacroSAN Technologies Co., Ltd. +# 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. + +NFS_NON_CONFIG = 0 +NFS_ENABLED = 1 +NFS_DISABLED = 2 +NFS_EXCEPTION = 3 +NFS_NON_SUPPORTED = 0 +NFS_SUPPORTED = 1 + +CIFS_SHARE_MODE = '2' +CIFS_ENABLED = '1' +CIFS_NON_CONFIG = '-1' +CIFS_DISABLED = '-2' +CIFS_EXCEPTION = '-3' + +USER_NOT_EXIST = '0' +USER_EXIST = '1' +USER_FORMAT_ERROR = '2' + +GROUP_NOT_EXIST = '0' +GROUP_EXIST = '1' +GROUP_FORMAT_ERROR = '2' + +CODE_SUCCESS = 0 +CODE_NOT_FOUND = 4 +CODE_SOURCE_NOT_EXIST = 403 + +TOKEN_EXPIRED = 301 +TOKEN_VERIFY_FAILED = 302 +TOKEN_FORMAT_ERROR = 303 +TOKEN_REQUIRED = 304 diff --git a/manila/share/drivers/macrosan/macrosan_helper.py b/manila/share/drivers/macrosan/macrosan_helper.py index 1008a217f5..1da89b435f 100644 --- a/manila/share/drivers/macrosan/macrosan_helper.py +++ b/manila/share/drivers/macrosan/macrosan_helper.py @@ -1,545 +1,545 @@ -# Copyright (c) 2022 MacroSAN Technologies Co., Ltd. -# 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 re - -from oslo_config import cfg -from oslo_log import log -from oslo_utils import units - -from manila import exception -from manila.i18n import _ -from manila.share.drivers.macrosan import macrosan_constants as constants -from manila.share.drivers.macrosan import rest_helper -from manila.share import utils as share_utils - -CONF = cfg.CONF - -LOG = log.getLogger(__name__) - - -class MacrosanHelper(object): - def __init__(self, configuration): - self.configuration = configuration - self.rest = rest_helper.RestHelper(self.configuration) - self.snapshot_support = False - self.replication_support = False - self.pools = self.configuration.macrosan_share_pools - - def check_share_service(self): - nfs_service = self.rest._get_nfs_service_status() - if nfs_service['serviceStatus'] not in [constants.NFS_NON_CONFIG, - constants.NFS_ENABLED, - constants.NFS_DISABLED]: - raise exception.MacrosanBackendExeption( - reason=_("nfs service exception. Please check backend")) - elif nfs_service['serviceStatus'] == constants.NFS_NON_CONFIG: - self.rest._config_nfs_service() - self.rest._start_nfs_service() - elif nfs_service['serviceStatus'] == constants.NFS_DISABLED: - if (nfs_service['nfs3Status'] == constants.NFS_NON_SUPPORTED and - nfs_service['nfs4Status'] == constants.NFS_NON_SUPPORTED): - self.rest._config_nfs_service() - self.rest._start_nfs_service() - else: - if (nfs_service['nfs3Status'] == constants.NFS_NON_SUPPORTED and - nfs_service['nfs4Status'] == constants.NFS_NON_SUPPORTED): - self.rest._config_nfs_service() - - cifs_status = self.rest._get_cifs_service_status() - if cifs_status == constants.CIFS_EXCEPTION: - raise exception.MacrosanBackendExeption( - reason=_("cifs service exception. Please check backend")) - elif cifs_status == constants.CIFS_NON_CONFIG: - """need config first, then start service""" - self.rest._config_cifs_service() - self.rest._start_cifs_service() - elif cifs_status == constants.CIFS_DISABLED: - self.rest._start_cifs_service() - status = self.rest._get_cifs_service_status() - if status == constants.CIFS_SHARE_MODE: - self.rest._config_cifs_service() - elif cifs_status == constants.CIFS_SHARE_MODE: - self.rest._config_cifs_service() - - def do_setup(self): - """get token""" - self.rest.login() - - def create_share(self, share, share_server=None): - """Create a share""" - pool_name, share_name, proto = self._get_share_instance_pnp(share) - share_size = ''.join((str(share['size']), 'GB')) - - # first create filesystem - self.rest._create_filesystem(fs_name=share_name, - pool_name=pool_name, - filesystem_quota=share_size) - - share_path = self._generate_share_path(share_name) - # second create filesystem dir - self.rest._create_filesystem_dir(share_path) - # third create nfs or cifs share - if proto == 'NFS': - self.rest._create_nfs_share(share_path=share_path) - else: - user_name = 'manilanobody' - user_passwd = 'manilanobody' - group_name = 'manilanobody' - ret = self._ensure_user(user_name, user_passwd, group_name) - if not ret: - self.rest._delete_filesystem(share_name) - raise exception.MacrosanBackendExeption( - reason=(_( - 'Failed to create share %(share)s. Reason: ' - 'username %(user_name)s error.') - % {'share': share_name, 'user_name': user_name})) - - rw_list = [user_name] - rw_list_type = ['0'] - self.rest._create_cifs_share(share_name=share_name, - share_path=share_path, - rw_list=rw_list, - rw_list_type=rw_list_type) - - location = self._get_location_path(share_path, share_name, proto) - return location - - def delete_share(self, share, share_server=None): - """Delete a share.""" - pool, share_name, proto = self._get_share_instance_pnp(share) - share_path = self._generate_share_path(share_name) - - backend_share = self._get_share(share_path, proto) - if not backend_share: - LOG.error(f'Share {share_name} not found.') - filesystem = self.rest._get_filesystem(share_name) - if filesystem: - self.rest._delete_filesystem(share_name) - else: - if proto == 'NFS': - self.rest._delete_nfs_share(share_path) - else: - self.rest._delete_cifs_share(share_name, share_path) - self.rest._delete_filesystem(share_name) - - def extend_share(self, share, new_size, share_server=None): - """Extend share""" - pool, share_name, proto = self._get_share_instance_pnp(share) - share_path = self._generate_share_path(share_name) - backend_share = self._get_share(share_path, proto) - if not backend_share: - msg = f"Can't find the share by share name: {share_name}." - msg = _(msg) - LOG.error(msg) - raise exception.ShareResourceNotFound(share_id=share['id']) - - # storage size logic already in manila/share/api.py extend func - # size param need unit - new_size = ''.join((str(new_size), 'GB')) - self.rest._update_share_size(share_name, new_size) - - def shrink_share(self, share, new_size, share_server=None): - """Shrink share""" - pool, share_name, proto = self._get_share_instance_pnp(share) - share_path = self._generate_share_path(share_name) - backend_share = self._get_share(share_path, proto) - if not backend_share: - msg = f"Can't find the share by share name: {share_name}." - msg = _(msg) - LOG.error(msg) - raise exception.ShareResourceNotFound(share_id=share['id']) - - filesystem_info = self.rest._get_filesystem(share_name) - used_size = self._unit_convert_toGB(filesystem_info['usedCapacity']) - if new_size <= used_size: - raise exception.ShareShrinkingPossibleDataLoss( - share_id=share['id']) - # storage size logic already in manila/share/api.py shrink func - new_size = ''.join((str(new_size), 'GB')) - self.rest._update_share_size(share_name, new_size) - - def ensure_share(self, share, share_server=None): - """Enusre that share is exported""" - pool, share_name, proto = self._get_share_instance_pnp(share) - share_path = self._generate_share_path(share_name) - backend_share = self._get_share(share_path, proto) - if not backend_share: - raise exception.ShareResourceNotFound(share_id=share['id']) - - location = self._get_location_path(share_path, share_name, proto) - return [location] - - def _allow_access(self, share, access, share_server=None): - """Allow access to the share.""" - pool, share_name, proto = self._get_share_instance_pnp(share) - share_path = self._generate_share_path(share_name) - access_level = access['access_level'] - share_id = share['id'] - if access_level not in ('rw', 'ro'): - raise exception.InvalidShareAccess( - reason=(_('Unsupported access level: %s.') % access_level)) - if proto == 'NFS': - self._allow_nfs_access(share_path, share_name, access, share_id) - elif proto == 'CIFS': - self._allow_cifs_access(share_path, share_name, access, share_id) - - def _allow_nfs_access(self, share_path, share_name, access, share_id): - """Allow nfs access.""" - access_type = access['access_type'] - access_to = access['access_to'] - access_level = access['access_level'] - # Only use 'ip', - # input "*" replace all, or ip 172.0.1.11 , - # or ip network segment 172.0.1.11/255.255.0.0 172.0.1.11/16 - if access_type != 'ip': - message = (_('NFS shares only allow IP access types. ' - 'access_type: %(access_type)s') % - {'access_type': access_type}) - raise exception.InvalidShareAccess(reason=message) - backend_share = self.rest._get_nfs_share(share_path) - if not backend_share: - msg = (_("Can't find the share by share name: %s.") - % share_name) - LOG.error(msg) - raise exception.ShareResourceNotFound(share_id=share_id) - - if access_to == '0.0.0.0/0': - access_to = '*' - share_client = self.rest._get_access_from_nfs_share(share_path, - access_to) - - if share_client: - if access_level != share_client['accessRight']: - self.rest._change_nfs_access_rest(share_path, - access_to, - access_level) - else: - self.rest._allow_nfs_access_rest(share_path, - access_to, - access_level) - - def _allow_cifs_access(self, share_path, share_name, access, share_id): - """Allow cifs access.""" - access_type = access['access_type'] - access_to = access['access_to'] - access_level = access['access_level'] - if access_type != 'user': - message = _('Only user access type is ' - 'allowed for CIFS shares.') - raise exception.InvalidShareAccess(reason=message) - - backend_share = self.rest._get_cifs_share(share_path) - if not backend_share: - msg = (_("Can't find the share by share name: %s.") - % share_name) - LOG.error(msg) - raise exception.ShareResourceNotFound(share_id=share_id) - - share_client = self.rest._get_access_from_cifs_share(share_path, - access_to) - if share_client: - if access_level != share_client['accessRight']: - self.rest._change_cifs_access_rest(share_path, - access_to, - access_level, - share_client['ugType']) - else: - self.rest._allow_cifs_access_rest(share_path, - access_to, - access_level) - - def _deny_access(self, share, access, share_server=None): - """Deny access to the share.""" - pool, share_name, proto = self._get_share_instance_pnp(share) - share_path = self._generate_share_path(share_name) - - if proto == 'NFS': - self._deny_nfs_access(share_path, share_name, access) - else: - self._deny_cifs_access(share_path, share_name, access) - - def _deny_nfs_access(self, share_path, share_name, access): - """Deny nfs access.""" - access_type = access['access_type'] - access_to = access['access_to'] - if access_type != 'ip': - LOG.error('Only IP access types are allowed ' - 'for NFS shares.') - return - if access_to == '0.0.0.0/0': - access_to = '*' - share_client = self.rest._get_access_from_nfs_share(share_path, - access_to) - if not share_client: - LOG.error(f'Could not list the share access for share ' - f'{share_name}') - return - self.rest._delete_nfs_access_rest(share_path, access_to) - - def _deny_cifs_access(self, share_path, share_name, access): - """Deny cifs access.""" - access_type = access['access_type'] - access_to = access['access_to'] - if access_type != 'user': - LOG.error('Only USER access types are allowed ' - 'for CIFS shares.') - return - share_client = self.rest._get_access_from_cifs_share(share_path, - access_to) - if not share_client: - LOG.error(f'Could not list the share access for share ' - f'{share_name}') - return - self.rest._delete_cifs_access_rest(share_path, - share_client['ugName'], - share_client['ugType']) - - def _clear_access(self, share, share_server=None): - """Remove all access rules of the share""" - pool, share_name, proto = self._get_share_instance_pnp(share) - share_path = self._generate_share_path(share_name) - access_list = self._get_all_access_from_share(share_path, proto) - if not access_list: - LOG.error(f'Could not list the share access for share ' - f'{share_name}') - return - - if proto == 'NFS': - for share_access in access_list: - # IPv4 Address Blocks Reserved for Documentation - if share_access['access_to'] == '192.0.2.0': - continue - self.rest._delete_nfs_access_rest(share_path, - share_access['access_to']) - elif proto == 'CIFS': - for share_access in access_list: - if (share_access['access_to'] == 'manilanobody' - and share_access['ugType'] == '0'): - continue - self.rest._delete_cifs_access_rest(share_path, - share_access['access_to'], - share_access['ugType']) - - def update_access(self, share, access_rules, add_rules, - delete_rules, share_server=None): - """Update access rules list.""" - access_updates = {} - if not (add_rules or delete_rules): - self._clear_access(share, share_server) - for access_rule in access_rules: - try: - self._allow_access(share, access_rule, share_server) - except exception.InvalidShareAccess as e: - msg = f'Failed to allow {access_rule["access_level"]} ' \ - f'access to {access_rule["access_to"]}, reason {e}' - msg = _(msg) - LOG.error(msg) - access_updates.update( - {access_rule['access_id']: {'state': 'error'}}) - else: - for access_rule in delete_rules: - self._deny_access(share, access_rule, share_server) - for access_rule in add_rules: - try: - self._allow_access(share, access_rule, share_server) - except exception.InvalidShareAccess as e: - msg = f'Failed to allow {access_rule["access_level"]} ' \ - f'access to {access_rule["access_to"]}, reason {e}' - msg = _(msg) - LOG.error(msg) - access_updates.update( - {access_rule['access_id']: {'state': 'error'}}) - return access_updates - - def _get_all_access_from_share(self, share_path, share_proto): - access_list = [] - if share_proto == 'NFS': - access_list = self.rest._get_all_nfs_access_rest(share_path) - elif share_proto == 'CIFS': - access_list = self.rest._get_all_cifs_access_rest(share_path) - return access_list - - def _ensure_user(self, user_name, user_passwd, group_name): - ret_user = self.rest._query_user(user_name) - if ret_user == constants.USER_NOT_EXIST: - ret_group = self.rest._query_group(group_name) - if ret_group not in [constants.GROUP_NOT_EXIST, - constants.GROUP_EXIST]: - msg = f'Failed to use group {group_name}' - msg = _(msg) - raise exception.InvalidInput(reason=msg) - elif ret_group == constants.GROUP_NOT_EXIST: - self.rest._add_localgroup(group_name) - self.rest._add_localuser(user_name, user_passwd, group_name) - return True - elif ret_user == constants.USER_EXIST: - return True - else: - return False - - def update_share_stats(self, dict_data): - """Update pools info""" - result = self.rest._get_all_pool() - dict_data["pools"] = [] - for pool_name in self.pools: - pool_capacity = self._get_pool_capacity(pool_name, result) - if pool_capacity: - pool = { - 'pool_name': pool_name, - 'total_capacity_gb': pool_capacity['totalcapacity'], - 'free_capacity_gb': pool_capacity['freecapacity'], - 'allocated_capacity_gb': - pool_capacity['allocatedcapacity'], - 'reserved_percentage': - self.configuration.reserved_share_percentage, - 'reserved_snapshot_percentage': - self.configuration - .reserved_share_from_snapshot_percentage - or self.configuration.reserved_share_percentage, - 'reserved_share_extend_percentage': - self.configuration.reserved_share_extend_percentage - or self.configuration.reserved_share_percentage, - 'dedupe': False, - 'compression': False, - 'qos': False, - 'thin_provisioning': False, - 'snapshot_support': self.snapshot_support, - 'create_share_from_snapshot_support': - self.snapshot_support, - } - - dict_data["pools"].append(pool) - - if not dict_data['pools']: - msg = _("StoragePool is None") - LOG.error(msg) - raise exception.InvalidInput(reason=msg) - - def _get_pool_capacity(self, pool_name, result): - """Get total,allocated,free capacity of the pools""" - pool_info = self._find_pool_info(pool_name, result) - - if pool_info: - total_capacity = int(self._unit_convert_toGB( - pool_info['totalcapacity'])) - free_capacity = int(self._unit_convert_toGB( - pool_info['freecapacity'])) - allocated_capacity = int(self._unit_convert_toGB( - pool_info['allocatedcapacity'])) - - pool_info['totalcapacity'] = total_capacity - pool_info['freecapacity'] = free_capacity - pool_info['allocatedcapacity'] = allocated_capacity - - return pool_info - - def _unit_convert_toGB(self, capacity): - """Convert unit to GB""" - capacity = capacity.upper() - - try: - # get unit char array and use join connect to string - unit = re.findall(r'[A-Z]', capacity) - unit = ''.join(unit) - except BaseException: - unit = '' - # get capacity size,unit is GB - capacity = capacity.replace(unit, '') - capacity = float(capacity) - if unit in ['B', '']: - capacity = capacity / units.Gi - elif unit in ['K', 'KB']: - capacity = capacity / units.Mi - elif unit in ['M', 'MB']: - capacity = capacity / units.Ki - elif unit in ['G', 'GB']: - capacity = capacity - elif unit in ['T', 'TB']: - capacity = capacity * units.Ki - elif unit in ['E', 'EB']: - capacity = capacity * units.Mi - - capacity = '%.0f' % capacity - - return float(capacity) - - def _generate_share_name(self, share): - share_name = 'manila_%s' % share['id'] - return self._format_name(share_name) - - def _format_name(self, name): - """format name to meet the backend requirements""" - name = name[0: 31] - name = name.replace('-', '_') - return name - - def _generate_share_path(self, share_name): - """add '/' as path""" - share_path = r'/%(path)s/%(dirName)s' % { - 'path': share_name.replace("-", "_"), - 'dirName': share_name.replace("-", "_") - } - return share_path - - def _get_location_path(self, share_path, share_name, share_proto, ip=None): - location = None - if ip is None: - ip = self.configuration.macrosan_nas_ip - share_proto = share_proto.upper() - if share_proto == 'NFS': - location = f'{ip}:{share_path}' - elif share_proto == 'CIFS': - location = f'\\\\{ip}\\{share_name}' - return location - - def _get_share_instance_pnp(self, share_instance): - - proto = share_instance['share_proto'].upper() - share_name = self._generate_share_name(share_instance) - pool = share_utils.extract_host(share_instance['host'], level='pool') - if not pool: - msg = _("Pool doesn't exist in host field.") - raise exception.InvalidHost(reason=msg) - - if proto != 'NFS' and proto != 'CIFS': - msg = f'Share protocol {proto} is not supported.' - msg = _(msg) - raise exception.MacrosanBackendExeption(reason=msg) - - return pool, share_name, proto - - def _get_share(self, share_path, proto): - return (self.rest._get_nfs_share(share_path) - if proto == 'NFS' - else self.rest._get_cifs_share(share_path)) - - def _find_pool_info(self, pool_name, result): - if pool_name is None: - return - pool_info = {} - pool_name = pool_name.strip() - - for item in result.get('data', []): - if pool_name == item['name']: - pool_info['name'] = item['name'] - pool_info['totalcapacity'] = item['size'] - pool_info['allocatedcapacity'] = item['allocated'] - pool_info['freecapacity'] = item['free'] - pool_info['health'] = item['health'] - pool_info['rw'] = item['rwStatus'] - break - - return pool_info +# Copyright (c) 2022 MacroSAN Technologies Co., Ltd. +# 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 re + +from oslo_config import cfg +from oslo_log import log +from oslo_utils import units + +from manila import exception +from manila.i18n import _ +from manila.share.drivers.macrosan import macrosan_constants as constants +from manila.share.drivers.macrosan import rest_helper +from manila.share import utils as share_utils + +CONF = cfg.CONF + +LOG = log.getLogger(__name__) + + +class MacrosanHelper(object): + def __init__(self, configuration): + self.configuration = configuration + self.rest = rest_helper.RestHelper(self.configuration) + self.snapshot_support = False + self.replication_support = False + self.pools = self.configuration.macrosan_share_pools + + def check_share_service(self): + nfs_service = self.rest._get_nfs_service_status() + if nfs_service['serviceStatus'] not in [constants.NFS_NON_CONFIG, + constants.NFS_ENABLED, + constants.NFS_DISABLED]: + raise exception.MacrosanBackendExeption( + reason=_("nfs service exception. Please check backend")) + elif nfs_service['serviceStatus'] == constants.NFS_NON_CONFIG: + self.rest._config_nfs_service() + self.rest._start_nfs_service() + elif nfs_service['serviceStatus'] == constants.NFS_DISABLED: + if (nfs_service['nfs3Status'] == constants.NFS_NON_SUPPORTED and + nfs_service['nfs4Status'] == constants.NFS_NON_SUPPORTED): + self.rest._config_nfs_service() + self.rest._start_nfs_service() + else: + if (nfs_service['nfs3Status'] == constants.NFS_NON_SUPPORTED and + nfs_service['nfs4Status'] == constants.NFS_NON_SUPPORTED): + self.rest._config_nfs_service() + + cifs_status = self.rest._get_cifs_service_status() + if cifs_status == constants.CIFS_EXCEPTION: + raise exception.MacrosanBackendExeption( + reason=_("cifs service exception. Please check backend")) + elif cifs_status == constants.CIFS_NON_CONFIG: + """need config first, then start service""" + self.rest._config_cifs_service() + self.rest._start_cifs_service() + elif cifs_status == constants.CIFS_DISABLED: + self.rest._start_cifs_service() + status = self.rest._get_cifs_service_status() + if status == constants.CIFS_SHARE_MODE: + self.rest._config_cifs_service() + elif cifs_status == constants.CIFS_SHARE_MODE: + self.rest._config_cifs_service() + + def do_setup(self): + """get token""" + self.rest.login() + + def create_share(self, share, share_server=None): + """Create a share""" + pool_name, share_name, proto = self._get_share_instance_pnp(share) + share_size = ''.join((str(share['size']), 'GB')) + + # first create filesystem + self.rest._create_filesystem(fs_name=share_name, + pool_name=pool_name, + filesystem_quota=share_size) + + share_path = self._generate_share_path(share_name) + # second create filesystem dir + self.rest._create_filesystem_dir(share_path) + # third create nfs or cifs share + if proto == 'NFS': + self.rest._create_nfs_share(share_path=share_path) + else: + user_name = 'manilanobody' + user_passwd = 'manilanobody' + group_name = 'manilanobody' + ret = self._ensure_user(user_name, user_passwd, group_name) + if not ret: + self.rest._delete_filesystem(share_name) + raise exception.MacrosanBackendExeption( + reason=(_( + 'Failed to create share %(share)s. Reason: ' + 'username %(user_name)s error.') + % {'share': share_name, 'user_name': user_name})) + + rw_list = [user_name] + rw_list_type = ['0'] + self.rest._create_cifs_share(share_name=share_name, + share_path=share_path, + rw_list=rw_list, + rw_list_type=rw_list_type) + + location = self._get_location_path(share_path, share_name, proto) + return location + + def delete_share(self, share, share_server=None): + """Delete a share.""" + pool, share_name, proto = self._get_share_instance_pnp(share) + share_path = self._generate_share_path(share_name) + + backend_share = self._get_share(share_path, proto) + if not backend_share: + LOG.error(f'Share {share_name} not found.') + filesystem = self.rest._get_filesystem(share_name) + if filesystem: + self.rest._delete_filesystem(share_name) + else: + if proto == 'NFS': + self.rest._delete_nfs_share(share_path) + else: + self.rest._delete_cifs_share(share_name, share_path) + self.rest._delete_filesystem(share_name) + + def extend_share(self, share, new_size, share_server=None): + """Extend share""" + pool, share_name, proto = self._get_share_instance_pnp(share) + share_path = self._generate_share_path(share_name) + backend_share = self._get_share(share_path, proto) + if not backend_share: + msg = f"Can't find the share by share name: {share_name}." + msg = _(msg) + LOG.error(msg) + raise exception.ShareResourceNotFound(share_id=share['id']) + + # storage size logic already in manila/share/api.py extend func + # size param need unit + new_size = ''.join((str(new_size), 'GB')) + self.rest._update_share_size(share_name, new_size) + + def shrink_share(self, share, new_size, share_server=None): + """Shrink share""" + pool, share_name, proto = self._get_share_instance_pnp(share) + share_path = self._generate_share_path(share_name) + backend_share = self._get_share(share_path, proto) + if not backend_share: + msg = f"Can't find the share by share name: {share_name}." + msg = _(msg) + LOG.error(msg) + raise exception.ShareResourceNotFound(share_id=share['id']) + + filesystem_info = self.rest._get_filesystem(share_name) + used_size = self._unit_convert_toGB(filesystem_info['usedCapacity']) + if new_size <= used_size: + raise exception.ShareShrinkingPossibleDataLoss( + share_id=share['id']) + # storage size logic already in manila/share/api.py shrink func + new_size = ''.join((str(new_size), 'GB')) + self.rest._update_share_size(share_name, new_size) + + def ensure_share(self, share, share_server=None): + """Enusre that share is exported""" + pool, share_name, proto = self._get_share_instance_pnp(share) + share_path = self._generate_share_path(share_name) + backend_share = self._get_share(share_path, proto) + if not backend_share: + raise exception.ShareResourceNotFound(share_id=share['id']) + + location = self._get_location_path(share_path, share_name, proto) + return [location] + + def _allow_access(self, share, access, share_server=None): + """Allow access to the share.""" + pool, share_name, proto = self._get_share_instance_pnp(share) + share_path = self._generate_share_path(share_name) + access_level = access['access_level'] + share_id = share['id'] + if access_level not in ('rw', 'ro'): + raise exception.InvalidShareAccess( + reason=(_('Unsupported access level: %s.') % access_level)) + if proto == 'NFS': + self._allow_nfs_access(share_path, share_name, access, share_id) + elif proto == 'CIFS': + self._allow_cifs_access(share_path, share_name, access, share_id) + + def _allow_nfs_access(self, share_path, share_name, access, share_id): + """Allow nfs access.""" + access_type = access['access_type'] + access_to = access['access_to'] + access_level = access['access_level'] + # Only use 'ip', + # input "*" replace all, or ip 172.0.1.11 , + # or ip network segment 172.0.1.11/255.255.0.0 172.0.1.11/16 + if access_type != 'ip': + message = (_('NFS shares only allow IP access types. ' + 'access_type: %(access_type)s') % + {'access_type': access_type}) + raise exception.InvalidShareAccess(reason=message) + backend_share = self.rest._get_nfs_share(share_path) + if not backend_share: + msg = (_("Can't find the share by share name: %s.") + % share_name) + LOG.error(msg) + raise exception.ShareResourceNotFound(share_id=share_id) + + if access_to == '0.0.0.0/0': + access_to = '*' + share_client = self.rest._get_access_from_nfs_share(share_path, + access_to) + + if share_client: + if access_level != share_client['accessRight']: + self.rest._change_nfs_access_rest(share_path, + access_to, + access_level) + else: + self.rest._allow_nfs_access_rest(share_path, + access_to, + access_level) + + def _allow_cifs_access(self, share_path, share_name, access, share_id): + """Allow cifs access.""" + access_type = access['access_type'] + access_to = access['access_to'] + access_level = access['access_level'] + if access_type != 'user': + message = _('Only user access type is ' + 'allowed for CIFS shares.') + raise exception.InvalidShareAccess(reason=message) + + backend_share = self.rest._get_cifs_share(share_path) + if not backend_share: + msg = (_("Can't find the share by share name: %s.") + % share_name) + LOG.error(msg) + raise exception.ShareResourceNotFound(share_id=share_id) + + share_client = self.rest._get_access_from_cifs_share(share_path, + access_to) + if share_client: + if access_level != share_client['accessRight']: + self.rest._change_cifs_access_rest(share_path, + access_to, + access_level, + share_client['ugType']) + else: + self.rest._allow_cifs_access_rest(share_path, + access_to, + access_level) + + def _deny_access(self, share, access, share_server=None): + """Deny access to the share.""" + pool, share_name, proto = self._get_share_instance_pnp(share) + share_path = self._generate_share_path(share_name) + + if proto == 'NFS': + self._deny_nfs_access(share_path, share_name, access) + else: + self._deny_cifs_access(share_path, share_name, access) + + def _deny_nfs_access(self, share_path, share_name, access): + """Deny nfs access.""" + access_type = access['access_type'] + access_to = access['access_to'] + if access_type != 'ip': + LOG.error('Only IP access types are allowed ' + 'for NFS shares.') + return + if access_to == '0.0.0.0/0': + access_to = '*' + share_client = self.rest._get_access_from_nfs_share(share_path, + access_to) + if not share_client: + LOG.error(f'Could not list the share access for share ' + f'{share_name}') + return + self.rest._delete_nfs_access_rest(share_path, access_to) + + def _deny_cifs_access(self, share_path, share_name, access): + """Deny cifs access.""" + access_type = access['access_type'] + access_to = access['access_to'] + if access_type != 'user': + LOG.error('Only USER access types are allowed ' + 'for CIFS shares.') + return + share_client = self.rest._get_access_from_cifs_share(share_path, + access_to) + if not share_client: + LOG.error(f'Could not list the share access for share ' + f'{share_name}') + return + self.rest._delete_cifs_access_rest(share_path, + share_client['ugName'], + share_client['ugType']) + + def _clear_access(self, share, share_server=None): + """Remove all access rules of the share""" + pool, share_name, proto = self._get_share_instance_pnp(share) + share_path = self._generate_share_path(share_name) + access_list = self._get_all_access_from_share(share_path, proto) + if not access_list: + LOG.error(f'Could not list the share access for share ' + f'{share_name}') + return + + if proto == 'NFS': + for share_access in access_list: + # IPv4 Address Blocks Reserved for Documentation + if share_access['access_to'] == '192.0.2.0': + continue + self.rest._delete_nfs_access_rest(share_path, + share_access['access_to']) + elif proto == 'CIFS': + for share_access in access_list: + if (share_access['access_to'] == 'manilanobody' + and share_access['ugType'] == '0'): + continue + self.rest._delete_cifs_access_rest(share_path, + share_access['access_to'], + share_access['ugType']) + + def update_access(self, share, access_rules, add_rules, + delete_rules, share_server=None): + """Update access rules list.""" + access_updates = {} + if not (add_rules or delete_rules): + self._clear_access(share, share_server) + for access_rule in access_rules: + try: + self._allow_access(share, access_rule, share_server) + except exception.InvalidShareAccess as e: + msg = f'Failed to allow {access_rule["access_level"]} ' \ + f'access to {access_rule["access_to"]}, reason {e}' + msg = _(msg) + LOG.error(msg) + access_updates.update( + {access_rule['access_id']: {'state': 'error'}}) + else: + for access_rule in delete_rules: + self._deny_access(share, access_rule, share_server) + for access_rule in add_rules: + try: + self._allow_access(share, access_rule, share_server) + except exception.InvalidShareAccess as e: + msg = f'Failed to allow {access_rule["access_level"]} ' \ + f'access to {access_rule["access_to"]}, reason {e}' + msg = _(msg) + LOG.error(msg) + access_updates.update( + {access_rule['access_id']: {'state': 'error'}}) + return access_updates + + def _get_all_access_from_share(self, share_path, share_proto): + access_list = [] + if share_proto == 'NFS': + access_list = self.rest._get_all_nfs_access_rest(share_path) + elif share_proto == 'CIFS': + access_list = self.rest._get_all_cifs_access_rest(share_path) + return access_list + + def _ensure_user(self, user_name, user_passwd, group_name): + ret_user = self.rest._query_user(user_name) + if ret_user == constants.USER_NOT_EXIST: + ret_group = self.rest._query_group(group_name) + if ret_group not in [constants.GROUP_NOT_EXIST, + constants.GROUP_EXIST]: + msg = f'Failed to use group {group_name}' + msg = _(msg) + raise exception.InvalidInput(reason=msg) + elif ret_group == constants.GROUP_NOT_EXIST: + self.rest._add_localgroup(group_name) + self.rest._add_localuser(user_name, user_passwd, group_name) + return True + elif ret_user == constants.USER_EXIST: + return True + else: + return False + + def update_share_stats(self, dict_data): + """Update pools info""" + result = self.rest._get_all_pool() + dict_data["pools"] = [] + for pool_name in self.pools: + pool_capacity = self._get_pool_capacity(pool_name, result) + if pool_capacity: + pool = { + 'pool_name': pool_name, + 'total_capacity_gb': pool_capacity['totalcapacity'], + 'free_capacity_gb': pool_capacity['freecapacity'], + 'allocated_capacity_gb': + pool_capacity['allocatedcapacity'], + 'reserved_percentage': + self.configuration.reserved_share_percentage, + 'reserved_snapshot_percentage': + self.configuration + .reserved_share_from_snapshot_percentage + or self.configuration.reserved_share_percentage, + 'reserved_share_extend_percentage': + self.configuration.reserved_share_extend_percentage + or self.configuration.reserved_share_percentage, + 'dedupe': False, + 'compression': False, + 'qos': False, + 'thin_provisioning': False, + 'snapshot_support': self.snapshot_support, + 'create_share_from_snapshot_support': + self.snapshot_support, + } + + dict_data["pools"].append(pool) + + if not dict_data['pools']: + msg = _("StoragePool is None") + LOG.error(msg) + raise exception.InvalidInput(reason=msg) + + def _get_pool_capacity(self, pool_name, result): + """Get total,allocated,free capacity of the pools""" + pool_info = self._find_pool_info(pool_name, result) + + if pool_info: + total_capacity = int(self._unit_convert_toGB( + pool_info['totalcapacity'])) + free_capacity = int(self._unit_convert_toGB( + pool_info['freecapacity'])) + allocated_capacity = int(self._unit_convert_toGB( + pool_info['allocatedcapacity'])) + + pool_info['totalcapacity'] = total_capacity + pool_info['freecapacity'] = free_capacity + pool_info['allocatedcapacity'] = allocated_capacity + + return pool_info + + def _unit_convert_toGB(self, capacity): + """Convert unit to GB""" + capacity = capacity.upper() + + try: + # get unit char array and use join connect to string + unit = re.findall(r'[A-Z]', capacity) + unit = ''.join(unit) + except BaseException: + unit = '' + # get capacity size,unit is GB + capacity = capacity.replace(unit, '') + capacity = float(capacity) + if unit in ['B', '']: + capacity = capacity / units.Gi + elif unit in ['K', 'KB']: + capacity = capacity / units.Mi + elif unit in ['M', 'MB']: + capacity = capacity / units.Ki + elif unit in ['G', 'GB']: + capacity = capacity + elif unit in ['T', 'TB']: + capacity = capacity * units.Ki + elif unit in ['E', 'EB']: + capacity = capacity * units.Mi + + capacity = '%.0f' % capacity + + return float(capacity) + + def _generate_share_name(self, share): + share_name = 'manila_%s' % share['id'] + return self._format_name(share_name) + + def _format_name(self, name): + """format name to meet the backend requirements""" + name = name[0: 31] + name = name.replace('-', '_') + return name + + def _generate_share_path(self, share_name): + """add '/' as path""" + share_path = r'/%(path)s/%(dirName)s' % { + 'path': share_name.replace("-", "_"), + 'dirName': share_name.replace("-", "_") + } + return share_path + + def _get_location_path(self, share_path, share_name, share_proto, ip=None): + location = None + if ip is None: + ip = self.configuration.macrosan_nas_ip + share_proto = share_proto.upper() + if share_proto == 'NFS': + location = f'{ip}:{share_path}' + elif share_proto == 'CIFS': + location = f'\\\\{ip}\\{share_name}' + return location + + def _get_share_instance_pnp(self, share_instance): + + proto = share_instance['share_proto'].upper() + share_name = self._generate_share_name(share_instance) + pool = share_utils.extract_host(share_instance['host'], level='pool') + if not pool: + msg = _("Pool doesn't exist in host field.") + raise exception.InvalidHost(reason=msg) + + if proto != 'NFS' and proto != 'CIFS': + msg = f'Share protocol {proto} is not supported.' + msg = _(msg) + raise exception.MacrosanBackendExeption(reason=msg) + + return pool, share_name, proto + + def _get_share(self, share_path, proto): + return (self.rest._get_nfs_share(share_path) + if proto == 'NFS' + else self.rest._get_cifs_share(share_path)) + + def _find_pool_info(self, pool_name, result): + if pool_name is None: + return + pool_info = {} + pool_name = pool_name.strip() + + for item in result.get('data', []): + if pool_name == item['name']: + pool_info['name'] = item['name'] + pool_info['totalcapacity'] = item['size'] + pool_info['allocatedcapacity'] = item['allocated'] + pool_info['freecapacity'] = item['free'] + pool_info['health'] = item['health'] + pool_info['rw'] = item['rwStatus'] + break + + return pool_info diff --git a/manila/share/drivers/macrosan/macrosan_nas.py b/manila/share/drivers/macrosan/macrosan_nas.py index 7b36849e7f..ff5c5fcc22 100644 --- a/manila/share/drivers/macrosan/macrosan_nas.py +++ b/manila/share/drivers/macrosan/macrosan_nas.py @@ -1,186 +1,186 @@ -# Copyright (c) 2022 MacroSAN Technologies Co., Ltd. -# 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. - -""" -Share driver for Macrosan Storage Array. -""" - -import functools -from oslo_config import cfg -from oslo_log import log - -from manila.share import driver -from manila.share.drivers.macrosan import macrosan_helper - -macrosan_opts = [ - cfg.HostAddressOpt('macrosan_nas_ip', - required=True, - help='IP address for the Macrosan NAS server.'), - cfg.PortOpt('macrosan_nas_port', - default=8443, - help='Port number for the Macrosan NAS server.'), - cfg.StrOpt('macrosan_nas_username', - default='manila', - help='Username for the Macrosan NAS server.'), - cfg.StrOpt('macrosan_nas_password', - default=None, - secret=True, - help='Password for the Macrosan NAS server.'), - cfg.StrOpt('macrosan_nas_http_protocol', - default='https', - choices=['http', 'https'], - help='Http protocol for the Macrosan NAS server.'), - cfg.BoolOpt('macrosan_ssl_cert_verify', - default=False, - help='Defines whether the driver should check ssl cert.'), - cfg.StrOpt('macrosan_nas_prefix', - default='nas', - help='Url prefix for the Macrosan NAS server.'), - cfg.ListOpt('macrosan_share_pools', - required=True, - help='Comma separated list of Macrosan NAS pools.'), - cfg.IntOpt('macrosan_timeout', - default=60, - help='request timeout in seconds.') -] - -CONF = cfg.CONF -CONF.register_opts(macrosan_opts) -LOG = log.getLogger(__name__) - - -def debug_trace(func): - """Log the dirver invoke method start and leave information - - Used in the MacrosanNasDriver class methods. - Ensure func have 'self' argument. - """ - - @functools.wraps(func) - def wrapper(*args, **kwargs): - driver = args[0] - method_name = "%(class_name)s.%(method)s" \ - % {"class_name": driver.__class__.__name__, - "method": func.__name__} - backend_name = driver.configuration.share_backend_name - LOG.debug("[%(backend_name)s]:Start %(method_name)s", - {"backend_name": backend_name, "method_name": method_name}) - result = func(*args, **kwargs) - LOG.debug("[%(backend_name)s]:Leave %(method_name)s", - {"backend_name": backend_name, "method_name": method_name}) - return result - - return wrapper - - -class MacrosanNasDriver(driver.ShareDriver): - """Macrosan Share Driver - - Driver version history: - V1.0.0: Initial version - Driver support: - share create/delete, - extend size, - shrink size, - update_access. - protocol: NFS/CIFS - - """ - - VENDOR = 'Macrosan' - VERSION = '1.0.0' - PROTOCOL = 'NFS_CIFS' - - def __init__(self, *args, **kwargs): - super(MacrosanNasDriver, self).__init__(False, *args, **kwargs) - self.configuration.append_config_values(macrosan_opts) - - self.helper = macrosan_helper.MacrosanHelper(self.configuration) - - @debug_trace - def do_setup(self, context): - """initialization the driver when start""" - self.helper.do_setup() - - @debug_trace - def check_for_setup_error(self): - """Check prerequisites""" - self.helper.check_share_service() - - @debug_trace - def create_share(self, context, share, share_server=None): - """Create a share""" - return self.helper.create_share(share, share_server) - - @debug_trace - def delete_share(self, context, share, share_server=None): - """Delete a share.""" - self.helper.delete_share(share, share_server) - - @debug_trace - def extend_share(self, share, new_size, share_server=None): - """Extend share capacity""" - self.helper.extend_share(share, new_size, share_server) - - @debug_trace - def shrink_share(self, share, new_size, share_server=None): - """Shrink share capacity""" - self.helper.shrink_share(share, new_size, share_server) - - @debug_trace - def ensure_share(self, context, share, share_server=None): - """Enusre that share is exported.""" - return self.helper.ensure_share(share, share_server) - - @debug_trace - def update_access(self, context, share, access_rules, add_rules, - delete_rules, share_server=None): - """Update access rules list. - - :param context: Current context - :param share: Share model with share data. - :param access_rules: All access rules for given share - :param add_rules: Empty List or List of access rules which should be - added. access_rules already contains these rules. - :param delete_rules: Empty List or List of access rules which should be - removed. access_rules doesn't contain these rules. - :param share_server: Not used by this driver. - - :returns: None, or a dictionary of ``access_id``, ``access_key`` as - key: value pairs for the rules added, where, ``access_id`` - is the UUID (string) of the access rule, and ``access_key`` - is the credential (string) of the entity granted access. - During recovery after error, the returned dictionary must - contain ``access_id``, ``access_key`` for all the rules that - the driver is ordered to resync, i.e. rules in the - ``access_rules`` parameter. - """ - return self.helper.update_access(share, access_rules, - add_rules, delete_rules, - share_server) - - @debug_trace - def _update_share_stats(self): - """Update backend status ,include driver and pools""" - - data = { - 'vendor_name': self.VENDOR, - 'driver_version': self.VERSION, - 'storage_protocol': self.PROTOCOL, - 'share_backend_name': - self.configuration.safe_get('share_backend_name'), - } - self.helper.update_share_stats(data) - super(MacrosanNasDriver, self)._update_share_stats(data) +# Copyright (c) 2022 MacroSAN Technologies Co., Ltd. +# 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. + +""" +Share driver for Macrosan Storage Array. +""" + +import functools +from oslo_config import cfg +from oslo_log import log + +from manila.share import driver +from manila.share.drivers.macrosan import macrosan_helper + +macrosan_opts = [ + cfg.HostAddressOpt('macrosan_nas_ip', + required=True, + help='IP address for the Macrosan NAS server.'), + cfg.PortOpt('macrosan_nas_port', + default=8443, + help='Port number for the Macrosan NAS server.'), + cfg.StrOpt('macrosan_nas_username', + default='manila', + help='Username for the Macrosan NAS server.'), + cfg.StrOpt('macrosan_nas_password', + default=None, + secret=True, + help='Password for the Macrosan NAS server.'), + cfg.StrOpt('macrosan_nas_http_protocol', + default='https', + choices=['http', 'https'], + help='Http protocol for the Macrosan NAS server.'), + cfg.BoolOpt('macrosan_ssl_cert_verify', + default=False, + help='Defines whether the driver should check ssl cert.'), + cfg.StrOpt('macrosan_nas_prefix', + default='nas', + help='Url prefix for the Macrosan NAS server.'), + cfg.ListOpt('macrosan_share_pools', + required=True, + help='Comma separated list of Macrosan NAS pools.'), + cfg.IntOpt('macrosan_timeout', + default=60, + help='request timeout in seconds.') +] + +CONF = cfg.CONF +CONF.register_opts(macrosan_opts) +LOG = log.getLogger(__name__) + + +def debug_trace(func): + """Log the dirver invoke method start and leave information + + Used in the MacrosanNasDriver class methods. + Ensure func have 'self' argument. + """ + + @functools.wraps(func) + def wrapper(*args, **kwargs): + driver = args[0] + method_name = "%(class_name)s.%(method)s" \ + % {"class_name": driver.__class__.__name__, + "method": func.__name__} + backend_name = driver.configuration.share_backend_name + LOG.debug("[%(backend_name)s]:Start %(method_name)s", + {"backend_name": backend_name, "method_name": method_name}) + result = func(*args, **kwargs) + LOG.debug("[%(backend_name)s]:Leave %(method_name)s", + {"backend_name": backend_name, "method_name": method_name}) + return result + + return wrapper + + +class MacrosanNasDriver(driver.ShareDriver): + """Macrosan Share Driver + + Driver version history: + V1.0.0: Initial version + Driver support: + share create/delete, + extend size, + shrink size, + update_access. + protocol: NFS/CIFS + + """ + + VENDOR = 'Macrosan' + VERSION = '1.0.0' + PROTOCOL = 'NFS_CIFS' + + def __init__(self, *args, **kwargs): + super(MacrosanNasDriver, self).__init__(False, *args, **kwargs) + self.configuration.append_config_values(macrosan_opts) + + self.helper = macrosan_helper.MacrosanHelper(self.configuration) + + @debug_trace + def do_setup(self, context): + """initialization the driver when start""" + self.helper.do_setup() + + @debug_trace + def check_for_setup_error(self): + """Check prerequisites""" + self.helper.check_share_service() + + @debug_trace + def create_share(self, context, share, share_server=None): + """Create a share""" + return self.helper.create_share(share, share_server) + + @debug_trace + def delete_share(self, context, share, share_server=None): + """Delete a share.""" + self.helper.delete_share(share, share_server) + + @debug_trace + def extend_share(self, share, new_size, share_server=None): + """Extend share capacity""" + self.helper.extend_share(share, new_size, share_server) + + @debug_trace + def shrink_share(self, share, new_size, share_server=None): + """Shrink share capacity""" + self.helper.shrink_share(share, new_size, share_server) + + @debug_trace + def ensure_share(self, context, share, share_server=None): + """Enusre that share is exported.""" + return self.helper.ensure_share(share, share_server) + + @debug_trace + def update_access(self, context, share, access_rules, add_rules, + delete_rules, share_server=None): + """Update access rules list. + + :param context: Current context + :param share: Share model with share data. + :param access_rules: All access rules for given share + :param add_rules: Empty List or List of access rules which should be + added. access_rules already contains these rules. + :param delete_rules: Empty List or List of access rules which should be + removed. access_rules doesn't contain these rules. + :param share_server: Not used by this driver. + + :returns: None, or a dictionary of ``access_id``, ``access_key`` as + key: value pairs for the rules added, where, ``access_id`` + is the UUID (string) of the access rule, and ``access_key`` + is the credential (string) of the entity granted access. + During recovery after error, the returned dictionary must + contain ``access_id``, ``access_key`` for all the rules that + the driver is ordered to resync, i.e. rules in the + ``access_rules`` parameter. + """ + return self.helper.update_access(share, access_rules, + add_rules, delete_rules, + share_server) + + @debug_trace + def _update_share_stats(self): + """Update backend status ,include driver and pools""" + + data = { + 'vendor_name': self.VENDOR, + 'driver_version': self.VERSION, + 'storage_protocol': self.PROTOCOL, + 'share_backend_name': + self.configuration.safe_get('share_backend_name'), + } + self.helper.update_share_stats(data) + super(MacrosanNasDriver, self)._update_share_stats(data) diff --git a/manila/share/drivers/macrosan/rest_helper.py b/manila/share/drivers/macrosan/rest_helper.py index ca8980a4b9..c918889ca8 100644 --- a/manila/share/drivers/macrosan/rest_helper.py +++ b/manila/share/drivers/macrosan/rest_helper.py @@ -1,668 +1,668 @@ -# Copyright (c) 2022 MacroSAN Technologies Co., Ltd. -# 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 requests - -from oslo_log import log - -from manila import exception -from manila.i18n import _ -from manila.share.drivers.macrosan import macrosan_constants as constants -from manila import utils - -LOG = log.getLogger(__name__) - - -class RestHelper(object): - def __init__(self, configuration): - self.configuration = configuration - self._protocol = self.configuration.macrosan_nas_http_protocol - self._ip = self.configuration.macrosan_nas_ip - self._port = self.configuration.macrosan_nas_port - self._prefix = self.configuration.macrosan_nas_prefix - self._token = None - self._username = self.configuration.macrosan_nas_username - self._password = self.configuration.macrosan_nas_password - self.request_timeout = self.configuration.macrosan_timeout - self.ssl_verify = self.configuration.macrosan_ssl_cert_verify - if not self.ssl_verify: - # Suppress the Insecure request warnings - requests.packages.urllib3.disable_warnings( - requests.packages.urllib3.exceptions.InsecureRequestWarning) - - @utils.synchronized('macrosan_manila') - def call(self, url, data, method): - """Send requests. - - If token is expired,re-login. - """ - header = {'Authorization': self._token} - if self._token is None: - self.login() - - result = self.do_request(url, data, method, header) - if result['code'] == constants.TOKEN_EXPIRED: - LOG.error("Token is expired, re-login.") - self.login() - # token refresh, Re-assign - header['Authorization'] = self._token - result = self.do_request(url, data, method, header) - elif (result['code'] == constants.TOKEN_FORMAT_ERROR or - result['code'] == constants.TOKEN_VERIFY_FAILED or - result['code'] == constants.TOKEN_REQUIRED): - msg = _('Token authentication error.') - LOG.error(msg) - raise exception.MacrosanBackendExeption(msg) - return result - - def do_request(self, url, data, method, header=None): - final_url = (f'{self._protocol}://{self._ip}:{self._port}/' - f'{self._prefix}/{url}') - LOG.debug(f'Request URL: {final_url}, Method: {method}, Data: {data}') - - if method == 'POST': - res = requests.post(final_url, data=data, headers=header, - timeout=self.request_timeout, - verify=self.ssl_verify) - elif method == 'GET': - res = requests.get(final_url, data=data, headers=header, - timeout=self.request_timeout, - verify=self.ssl_verify) - elif method == 'PUT': - res = requests.put(final_url, data=data, headers=header, - timeout=self.request_timeout, - verify=self.ssl_verify) - elif method == 'DELETE': - res = requests.delete(final_url, data=data, headers=header, - timeout=self.request_timeout, - verify=self.ssl_verify) - else: - msg = (_("Request method %s invalid.") % method) - raise exception.ShareBackendException(msg=msg) - - code = res.status_code - if code != 200: - msg = (_('Code: %(code)s, URL: %(url)s, Message: %(msg)s') - % {'code': res.status_code, - 'url': final_url, - 'msg': res.text}) - LOG.error(msg) - raise exception.NetworkException(msg) - response = res.json() - LOG.debug('CODE: %(code)s, RESPONSE: %(response)s', - {'code': code, 'response': response}) - return response - - def login(self): - """Login array and return token.""" - url = 'rest/token' - - data = {'userName': self._username, - 'userPasswd': self._password} - result = self.do_request(url, data, 'POST') - if result['code'] != 0: - msg = f"Login failed. code: {result['code']}" - msg = _(msg) - LOG.error(msg) - raise exception.ShareBackendException(msg=msg) - LOG.debug(f'Login successful. URL {self._ip}\n') - self._token = result['data'] - - def _assert_result_code(self, result, msg): - if (result['code'] != constants.CODE_SUCCESS - and result['code'] != constants.CODE_NOT_FOUND): - error_msg = (_('%(err)s\nresult: %(res)s.') % {'err': msg, - 'res': result}) - LOG.error(error_msg) - raise exception.ShareBackendException(msg=error_msg) - - def _assert_result_data(self, result, msg): - if "data" not in result: - error_msg = (_('Error:"data" not in result. %s') % msg) - LOG.error(error_msg) - raise exception.ShareBackendException(msg=error_msg) - - def _create_nfs_share(self, share_path): - url = 'rest/nfsShare' - # IPv4 Address Blocks Reserved for Documentation - params = { - 'path': share_path, - 'authority': 'ro', - 'accessClient': '192.0.2.0', - } - result = self.call(url, params, 'POST') - - msg = 'Failed to create a nfs share.' - self._assert_result_code(result, msg) - - def _get_nfs_share(self, share_path): - # GET method: param need be after url - url = f'rest/nfsShare?path={share_path}' - result = self.call(url, None, 'GET') - - msg = 'Failed to get nfs share.' - self._assert_result_code(result, msg) - return result['data'] - - def _delete_nfs_share(self, share_path): - url = f'rest/nfsShare?path={share_path}' - result = self.call(url, None, 'DELETE') - - msg = 'Failed to delete nfs share.' - self._assert_result_code(result, msg) - - def _create_cifs_share(self, share_name, share_path, - rw_list, rw_list_type): - url = 'rest/cifsShare' - - params = { - 'path': share_path, - 'cifsName': share_name, - 'cifsDescription': '', - 'RoList': [], - 'RoListType': [], - 'RwList': rw_list, - 'RwListType': rw_list_type, - 'allowList': [], - 'denyList': [], - } - result = self.call(url, params, 'POST') - - msg = 'Failed to create a CIFS share.' - self._assert_result_code(result, msg) - - def _get_cifs_share(self, share_path): - url = f'rest/cifsShare?path={share_path}' - result = self.call(url, None, 'GET') - - msg = 'Failed to get the cifs share.' - self._assert_result_code(result, msg) - return result['data'] - - def _delete_cifs_share(self, share_name, share_path): - url = f'rest/cifsShare?path={share_path}&cifsName={share_name}' - result = self.call(url, None, 'DELETE') - msg = 'Failed to delete the cifs share.' - - self._assert_result_code(result, msg) - - def _update_share_size(self, fs_name, new_size): - url = f'rest/filesystem/{fs_name}' - - params = { - 'capacity': new_size, - } - - result = self.call(url, params, 'PUT') - msg = 'Failed to update the filesystem size.' - - self._assert_result_code(result, msg) - - def _create_filesystem(self, fs_name, pool_name, filesystem_quota): - url = 'rest/filesystem' - fsinfo = { - 'fsName': fs_name, - 'poolName': pool_name, - 'createType': '0', - 'fileSystemQuota': filesystem_quota, - 'fileSystemReserve': filesystem_quota, - 'wormStatus': 0, - 'defaultTimeStatus': 0, - 'defaultTimeNum': 0, - 'defaultTimeUnit': 'year', - 'isAutoLock': 0, - 'isAutoDelete': 0, - 'lockTime': 0 - } - result = self.call(url, fsinfo, 'POST') - - msg = 'Failed to create the filesystem.' - self._assert_result_code(result, msg) - - def _delete_filesystem(self, fs_name): - """Delete filesystem""" - url = f'rest/filesystem/{fs_name}' - result = self.call(url, None, 'DELETE') - - msg = 'Failed to delete the filesystem.' - self._assert_result_code(result, msg) - - def _get_filesystem(self, fs_name): - """Get filesystem """ - url = f'rest/filesystem/{fs_name}' - result = self.call(url, None, 'GET') - - msg = 'Failed to get the filesystem.' - self._assert_result_code(result, msg) - - return result['data'] - - def _create_filesystem_dir(self, share_path): - url = 'rest/fileDir' - slash = share_path.index(r'/', 1) - dir_info = { - 'path': share_path[0: slash], - 'dirName': share_path[slash + 1:], - } - result = self.call(url, dir_info, 'POST') - - msg = 'Failed to create the filesystem directory.' - self._assert_result_code(result, msg) - - def _delete_filesystem_dir(self, share_path): - slash = share_path.index(r'/', 1) - url = f'rest/fileDir?path={share_path[0: slash]}' \ - f'&dirName={share_path[slash + 1:]}' - - result = self.call(url, None, 'DELETE') - - msg = 'Failed to delete the filesystem directory.' - self._assert_result_code(result, msg) - - def _allow_access_rest(self, share_path, access_to, - access_level, share_proto): - """Allow access to the share.""" - if share_proto == 'NFS': - self._allow_nfs_access_rest(share_path, access_to, access_level) - elif share_proto == 'CIFS': - self._allow_cifs_access_rest(share_path, access_to, access_level) - else: - raise exception.InvalidInput( - reason=(_('Invalid Nas protocol: %s.') % share_proto)) - - def _allow_nfs_access_rest(self, share_path, access_to, access_level): - url = 'rest/nfsShareClient' - access = { - 'path': share_path, - 'client': access_to, - 'authority': access_level, - } - result = self.call(url, access, 'POST') - - msg = 'Failed to allow access to the NFS share.' - self._assert_result_code(result, msg) - - def _allow_cifs_access_rest(self, share_path, access_to, access_level): - url = 'rest/cifsShareClient' - ug_type = { - 'localUser': '0', - 'localGroup': '1', - 'adUser': '2', - 'adGroup': '3', - } - - msg = 'Failed to allow access to the CIFS share.' - access_info = (f'Access info (access_to: {access_to},' - f'access_level: {access_level},' - f'path: {share_path}.)') - - def send_rest(rest_access_to, rest_ug_type): - access = { - 'path': share_path, - 'right': access_level, - 'ugName': rest_access_to, - 'ugType': rest_ug_type, - } - result = self.call(url, access, 'POST') - err_code = result['code'] - if err_code == constants.CODE_SUCCESS: - return True - elif err_code != constants.CODE_SOURCE_NOT_EXIST: - self._assert_result_code(result, msg) - return False - - if '/' not in access_to: - # First, try to add local user access - LOG.debug('Attempting to add local user access. %s', access_info) - if send_rest(access_to, ug_type['localUser']): - return - # Second,If add local user access failed, - # try to add local group access - LOG.debug('Failed add local user access,' - ' attempting to add local group access. %s', access_info) - if send_rest(access_to, ug_type['localGroup']): - return - else: - str = access_to.index('/') - access_to = access_to[str + 1:] - # First, add domain user access - LOG.debug('Attempting to add domain user access. %s', access_info) - if send_rest(access_to, ug_type['adUser']): - return - # Second, if add domain user access failed, - # try to add domain group access. - LOG.debug('Failed add domain user access, ' - 'attempting to add domain group access. %s', access_info) - if send_rest(access_to, ug_type['adGroup']): - return - - raise exception.InvalidShare(reason=msg) - - def _get_access_from_nfs_share(self, path, clientName): - url = f'rest/nfsShareClient?path={path}&client={clientName}' - - result = self.call(url, None, 'GET') - msg = 'Failed to get share NFS access.' - - self._assert_result_code(result, msg) - share_client = None - if result['data'] is not None: - share_client = {} - share_client['path'] = result['data']['path'] - share_client['clientName'] = result['data']['clientName'] - share_client['accessRight'] = result['data']['accessRight'] - - return share_client - - def _get_access_from_cifs_share(self, share_path, access_to, - ug_input_type=None): - - ug_type = { - 'localUser': '0', - 'localGroup': '1', - 'adUser': '2', - 'adGroup': '3', - } - - msg = 'Failed to get share cifs access.' - access_info = (f'Access info (access_to: {access_to},' - f'path: {share_path}.)') - - def send_rest(access_to, ug_type): - url = f'rest/cifsShareClient?path={share_path}' \ - f'&ugName={access_to}&ugType={ug_type}' - result = self.call(url, None, 'GET') - self._assert_result_code(result, msg) - return result - - share_client = None - if ug_input_type is not None: - ret = send_rest(access_to, ug_input_type) - if ret['data']: - share_client = {} - share_client['path'] = ret['data']['path'] - share_client['ugName'] = ret['data']['ugName'] - share_client['ugType'] = ret['data']['ugType'] - share_client['accessRight'] = ret['data']['accessRight'] - - return share_client - elif '/' not in access_to: - LOG.debug('Attempting to get local user access. %s', access_info) - user_ret = send_rest(access_to, ug_type['localUser']) - if user_ret['code'] == constants.CODE_NOT_FOUND: - return share_client - if user_ret['data']: - share_client = {} - share_client['path'] = user_ret['data']['path'] - share_client['ugName'] = user_ret['data']['ugName'] - share_client['ugType'] = user_ret['data']['ugType'] - share_client['accessRight'] = user_ret['data']['accessRight'] - return share_client - - LOG.debug('Failed get local user access,' - ' attempting to get local group access. %s', access_info) - group_ret = send_rest(access_to, ug_type['localGroup']) - if group_ret['data']: - share_client = {} - share_client['path'] = group_ret['data']['path'] - share_client['ugName'] = group_ret['data']['ugName'] - share_client['ugType'] = group_ret['data']['ugType'] - share_client['accessRight'] = group_ret['data']['accessRight'] - return share_client - else: - str = access_to.index('/') - access_to = access_to[str + 1:] - LOG.debug('Attempting to get domain user access. %s', access_info) - aduser_ret = send_rest(access_to, ug_type['adUser']) - if aduser_ret['code'] == constants.CODE_NOT_FOUND: - return share_client - if aduser_ret['data']: - share_client = {} - share_client['path'] = aduser_ret['data']['path'] - share_client['ugName'] = aduser_ret['data']['ugName'] - share_client['ugType'] = aduser_ret['data']['ugType'] - share_client['accessRight'] = \ - aduser_ret['data']['accessRight'] - return share_client - - LOG.debug('Failed get domain user access,' - ' attempting to get domain group access. %s', - access_info) - adgroup_ret = send_rest(access_to, ug_type['adGroup']) - if adgroup_ret['data']: - share_client = {} - share_client['path'] = adgroup_ret['data']['path'] - share_client['ugName'] = adgroup_ret['data']['ugName'] - share_client['ugType'] = adgroup_ret['data']['ugType'] - share_client['accessRight'] = \ - adgroup_ret['data']['accessRight'] - return share_client - - return share_client - - def _get_all_nfs_access_rest(self, share_path): - url = f'rest/allNfsShareClient?path={share_path}' - - result = self.call(url, None, 'GET') - - msg = 'Get all nfs access error.' - self._assert_result_code(result, msg) - access_list = [] - if result['data'] is None: - pass - else: - for item in result.get('data', []): - access = {} - access['share_path'] = item['path'] - access['access_to'] = item['clientName'] - access['access_level'] = item['accessRight'] - access_list.append(access) - - return access_list - - def _get_all_cifs_access_rest(self, share_path): - url = f'rest/allCifsShareClient?path={share_path}' - - result = self.call(url, None, 'GET') - - msg = 'Get all cifs access error.' - self._assert_result_code(result, msg) - access_list = [] - for item in result.get('data', []): - access = {} - access['share_path'] = item['path'] - access['access_to'] = item['ugName'] - access['ugType'] = item['ugType'] - access['access_level'] = item['accessRight'] - access_list.append(access) - - return access_list - - def _change_nfs_access_rest(self, share_path, access_to, access_level): - url = 'rest/nfsShareClient' - access_info = { - 'path': share_path, - 'oldNfsClientName': access_to, - 'clientName': '', - 'accessRight': access_level, - 'allSquash': '', - 'rootSquash': '', - 'secure': '', - 'anonuid': '', - 'anongid': '', - } - result = self.call(url, access_info, 'PUT') - - msg = 'Update nfs acess error.' - self._assert_result_code(result, msg) - - def _change_cifs_access_rest(self, share_path, access_to, - access_level, ug_type): - url = 'rest/cifsShareClient' - if '/' in access_to: - str = access_to.index('/') - access_to = access_to[str + 1:] - access_info = { - 'path': share_path, - 'right': access_level, - 'ugName': access_to, - 'ugType': ug_type, - } - - result = self.call(url, access_info, 'PUT') - msg = 'Update cifs access error.' - - self._assert_result_code(result, msg) - - def _delete_nfs_access_rest(self, share_path, access_to): - url = f'rest/nfsShareClient?path={share_path}&client={access_to}' - - result = self.call(url, None, 'DELETE') - msg = 'Delete nfs access error.' - - self._assert_result_code(result, msg) - - def _delete_cifs_access_rest(self, share_path, access_to, ug_type): - url = f'rest/cifsShareClient?path={share_path}&ugName={access_to}' \ - f'&ugType={ug_type}' - - result = self.call(url, None, 'DELETE') - msg = 'Delete cifs access error.' - - self._assert_result_code(result, msg) - - def _get_nfs_service_status(self): - url = 'rest/nfsService' - result = self.call(url, None, 'GET') - - msg = 'Get NFS service stauts error.' - self._assert_result_code(result, msg) - - nfs_service = {} - - nfs_service['serviceStatus'] = result['data']['serviceStatus'] - nfs_service['nfs3Status'] = result['data']['nfs3Status'] - nfs_service['nfs4Status'] = result['data']['nfs4Status'] - - return nfs_service - - def _start_nfs_service(self): - url = 'rest/nfsService' - nfs_service_info = { - "openStatus": "1", - } - - result = self.call(url, nfs_service_info, 'PUT') - - self._assert_result_code(result, 'Start NFS service error.') - - def _config_nfs_service(self): - url = 'rest/nfsConfig' - config_nfs = { - 'configNfs3': "yes", - 'configNfs4': "yes", - } - - result = self.call(url, config_nfs, 'PUT') - - self._assert_result_code(result, 'Config NFS service error.') - - def _get_cifs_service_status(self): - url = 'rest/cifsService' - result = self.call(url, None, 'GET') - - msg = 'Get CIFS service status error.' - self._assert_result_code(result, msg) - - return result['data'] - - def _start_cifs_service(self): - url = 'rest/cifsService' - cifs_service_info = { - 'openStatus': '1', - } - - result = self.call(url, cifs_service_info, 'PUT') - - self._assert_result_code(result, 'Start CIFS service error.') - - def _config_cifs_service(self): - url = 'rest/cifsConfig' - """config user mode""" - config_cifs = { - 'workName': 'manila', - 'description': '', - 'access_way': 'user', - 'isCache': 'no', - 'adsName': '', - 'adsIP': '', - 'adsUSER': '', - 'adsPASSWD': '', - 'allowList': [], - 'denyList': [], - } - - result = self.call(url, config_cifs, 'PUT') - - self._assert_result_code(result, 'Config CIFS service error.') - - def _get_all_pool(self): - url = 'rest/storagepool' - - result = self.call(url, None, 'GET') - - msg = 'Query pool info error.' - self._assert_result_code(result, msg) - - return result - - def _query_user(self, user_name): - url = f'rest/user/{user_name}' - - result = self.call(url, None, 'GET') - - msg = 'Query user error.' - self._assert_result_code(result, msg) - return result['data'] - - def _add_localuser(self, user_name, user_passwd, group_name): - url = 'rest/localUser' - user_info = { - 'userName': user_name, - 'mgGroup': group_name, - 'userPasswd': user_passwd, - 'unusedGroup': [], - } - result = self.call(url, user_info, 'POST') - msg = 'add localuser error.' - self._assert_result_code(result, msg) - - def _query_group(self, group_name): - url = f'rest/group/{group_name}' - - result = self.call(url, None, 'GET') - msg = 'Query group error.' - self._assert_result_code(result, msg) - return result['data'] - - def _add_localgroup(self, group_name): - url = 'rest/localGroup' - group_info = { - 'groupName': group_name, - } - result = self.call(url, group_info, 'POST') - - msg = 'add localgroup error.' - self._assert_result_code(result, msg) +# Copyright (c) 2022 MacroSAN Technologies Co., Ltd. +# 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 requests + +from oslo_log import log + +from manila import exception +from manila.i18n import _ +from manila.share.drivers.macrosan import macrosan_constants as constants +from manila import utils + +LOG = log.getLogger(__name__) + + +class RestHelper(object): + def __init__(self, configuration): + self.configuration = configuration + self._protocol = self.configuration.macrosan_nas_http_protocol + self._ip = self.configuration.macrosan_nas_ip + self._port = self.configuration.macrosan_nas_port + self._prefix = self.configuration.macrosan_nas_prefix + self._token = None + self._username = self.configuration.macrosan_nas_username + self._password = self.configuration.macrosan_nas_password + self.request_timeout = self.configuration.macrosan_timeout + self.ssl_verify = self.configuration.macrosan_ssl_cert_verify + if not self.ssl_verify: + # Suppress the Insecure request warnings + requests.packages.urllib3.disable_warnings( + requests.packages.urllib3.exceptions.InsecureRequestWarning) + + @utils.synchronized('macrosan_manila') + def call(self, url, data, method): + """Send requests. + + If token is expired,re-login. + """ + header = {'Authorization': self._token} + if self._token is None: + self.login() + + result = self.do_request(url, data, method, header) + if result['code'] == constants.TOKEN_EXPIRED: + LOG.error("Token is expired, re-login.") + self.login() + # token refresh, Re-assign + header['Authorization'] = self._token + result = self.do_request(url, data, method, header) + elif (result['code'] == constants.TOKEN_FORMAT_ERROR or + result['code'] == constants.TOKEN_VERIFY_FAILED or + result['code'] == constants.TOKEN_REQUIRED): + msg = _('Token authentication error.') + LOG.error(msg) + raise exception.MacrosanBackendExeption(msg) + return result + + def do_request(self, url, data, method, header=None): + final_url = (f'{self._protocol}://{self._ip}:{self._port}/' + f'{self._prefix}/{url}') + LOG.debug(f'Request URL: {final_url}, Method: {method}, Data: {data}') + + if method == 'POST': + res = requests.post(final_url, data=data, headers=header, + timeout=self.request_timeout, + verify=self.ssl_verify) + elif method == 'GET': + res = requests.get(final_url, data=data, headers=header, + timeout=self.request_timeout, + verify=self.ssl_verify) + elif method == 'PUT': + res = requests.put(final_url, data=data, headers=header, + timeout=self.request_timeout, + verify=self.ssl_verify) + elif method == 'DELETE': + res = requests.delete(final_url, data=data, headers=header, + timeout=self.request_timeout, + verify=self.ssl_verify) + else: + msg = (_("Request method %s invalid.") % method) + raise exception.ShareBackendException(msg=msg) + + code = res.status_code + if code != 200: + msg = (_('Code: %(code)s, URL: %(url)s, Message: %(msg)s') + % {'code': res.status_code, + 'url': final_url, + 'msg': res.text}) + LOG.error(msg) + raise exception.NetworkException(msg) + response = res.json() + LOG.debug('CODE: %(code)s, RESPONSE: %(response)s', + {'code': code, 'response': response}) + return response + + def login(self): + """Login array and return token.""" + url = 'rest/token' + + data = {'userName': self._username, + 'userPasswd': self._password} + result = self.do_request(url, data, 'POST') + if result['code'] != 0: + msg = f"Login failed. code: {result['code']}" + msg = _(msg) + LOG.error(msg) + raise exception.ShareBackendException(msg=msg) + LOG.debug(f'Login successful. URL {self._ip}\n') + self._token = result['data'] + + def _assert_result_code(self, result, msg): + if (result['code'] != constants.CODE_SUCCESS + and result['code'] != constants.CODE_NOT_FOUND): + error_msg = (_('%(err)s\nresult: %(res)s.') % {'err': msg, + 'res': result}) + LOG.error(error_msg) + raise exception.ShareBackendException(msg=error_msg) + + def _assert_result_data(self, result, msg): + if "data" not in result: + error_msg = (_('Error:"data" not in result. %s') % msg) + LOG.error(error_msg) + raise exception.ShareBackendException(msg=error_msg) + + def _create_nfs_share(self, share_path): + url = 'rest/nfsShare' + # IPv4 Address Blocks Reserved for Documentation + params = { + 'path': share_path, + 'authority': 'ro', + 'accessClient': '192.0.2.0', + } + result = self.call(url, params, 'POST') + + msg = 'Failed to create a nfs share.' + self._assert_result_code(result, msg) + + def _get_nfs_share(self, share_path): + # GET method: param need be after url + url = f'rest/nfsShare?path={share_path}' + result = self.call(url, None, 'GET') + + msg = 'Failed to get nfs share.' + self._assert_result_code(result, msg) + return result['data'] + + def _delete_nfs_share(self, share_path): + url = f'rest/nfsShare?path={share_path}' + result = self.call(url, None, 'DELETE') + + msg = 'Failed to delete nfs share.' + self._assert_result_code(result, msg) + + def _create_cifs_share(self, share_name, share_path, + rw_list, rw_list_type): + url = 'rest/cifsShare' + + params = { + 'path': share_path, + 'cifsName': share_name, + 'cifsDescription': '', + 'RoList': [], + 'RoListType': [], + 'RwList': rw_list, + 'RwListType': rw_list_type, + 'allowList': [], + 'denyList': [], + } + result = self.call(url, params, 'POST') + + msg = 'Failed to create a CIFS share.' + self._assert_result_code(result, msg) + + def _get_cifs_share(self, share_path): + url = f'rest/cifsShare?path={share_path}' + result = self.call(url, None, 'GET') + + msg = 'Failed to get the cifs share.' + self._assert_result_code(result, msg) + return result['data'] + + def _delete_cifs_share(self, share_name, share_path): + url = f'rest/cifsShare?path={share_path}&cifsName={share_name}' + result = self.call(url, None, 'DELETE') + msg = 'Failed to delete the cifs share.' + + self._assert_result_code(result, msg) + + def _update_share_size(self, fs_name, new_size): + url = f'rest/filesystem/{fs_name}' + + params = { + 'capacity': new_size, + } + + result = self.call(url, params, 'PUT') + msg = 'Failed to update the filesystem size.' + + self._assert_result_code(result, msg) + + def _create_filesystem(self, fs_name, pool_name, filesystem_quota): + url = 'rest/filesystem' + fsinfo = { + 'fsName': fs_name, + 'poolName': pool_name, + 'createType': '0', + 'fileSystemQuota': filesystem_quota, + 'fileSystemReserve': filesystem_quota, + 'wormStatus': 0, + 'defaultTimeStatus': 0, + 'defaultTimeNum': 0, + 'defaultTimeUnit': 'year', + 'isAutoLock': 0, + 'isAutoDelete': 0, + 'lockTime': 0 + } + result = self.call(url, fsinfo, 'POST') + + msg = 'Failed to create the filesystem.' + self._assert_result_code(result, msg) + + def _delete_filesystem(self, fs_name): + """Delete filesystem""" + url = f'rest/filesystem/{fs_name}' + result = self.call(url, None, 'DELETE') + + msg = 'Failed to delete the filesystem.' + self._assert_result_code(result, msg) + + def _get_filesystem(self, fs_name): + """Get filesystem """ + url = f'rest/filesystem/{fs_name}' + result = self.call(url, None, 'GET') + + msg = 'Failed to get the filesystem.' + self._assert_result_code(result, msg) + + return result['data'] + + def _create_filesystem_dir(self, share_path): + url = 'rest/fileDir' + slash = share_path.index(r'/', 1) + dir_info = { + 'path': share_path[0: slash], + 'dirName': share_path[slash + 1:], + } + result = self.call(url, dir_info, 'POST') + + msg = 'Failed to create the filesystem directory.' + self._assert_result_code(result, msg) + + def _delete_filesystem_dir(self, share_path): + slash = share_path.index(r'/', 1) + url = f'rest/fileDir?path={share_path[0: slash]}' \ + f'&dirName={share_path[slash + 1:]}' + + result = self.call(url, None, 'DELETE') + + msg = 'Failed to delete the filesystem directory.' + self._assert_result_code(result, msg) + + def _allow_access_rest(self, share_path, access_to, + access_level, share_proto): + """Allow access to the share.""" + if share_proto == 'NFS': + self._allow_nfs_access_rest(share_path, access_to, access_level) + elif share_proto == 'CIFS': + self._allow_cifs_access_rest(share_path, access_to, access_level) + else: + raise exception.InvalidInput( + reason=(_('Invalid Nas protocol: %s.') % share_proto)) + + def _allow_nfs_access_rest(self, share_path, access_to, access_level): + url = 'rest/nfsShareClient' + access = { + 'path': share_path, + 'client': access_to, + 'authority': access_level, + } + result = self.call(url, access, 'POST') + + msg = 'Failed to allow access to the NFS share.' + self._assert_result_code(result, msg) + + def _allow_cifs_access_rest(self, share_path, access_to, access_level): + url = 'rest/cifsShareClient' + ug_type = { + 'localUser': '0', + 'localGroup': '1', + 'adUser': '2', + 'adGroup': '3', + } + + msg = 'Failed to allow access to the CIFS share.' + access_info = (f'Access info (access_to: {access_to},' + f'access_level: {access_level},' + f'path: {share_path}.)') + + def send_rest(rest_access_to, rest_ug_type): + access = { + 'path': share_path, + 'right': access_level, + 'ugName': rest_access_to, + 'ugType': rest_ug_type, + } + result = self.call(url, access, 'POST') + err_code = result['code'] + if err_code == constants.CODE_SUCCESS: + return True + elif err_code != constants.CODE_SOURCE_NOT_EXIST: + self._assert_result_code(result, msg) + return False + + if '/' not in access_to: + # First, try to add local user access + LOG.debug('Attempting to add local user access. %s', access_info) + if send_rest(access_to, ug_type['localUser']): + return + # Second,If add local user access failed, + # try to add local group access + LOG.debug('Failed add local user access,' + ' attempting to add local group access. %s', access_info) + if send_rest(access_to, ug_type['localGroup']): + return + else: + str = access_to.index('/') + access_to = access_to[str + 1:] + # First, add domain user access + LOG.debug('Attempting to add domain user access. %s', access_info) + if send_rest(access_to, ug_type['adUser']): + return + # Second, if add domain user access failed, + # try to add domain group access. + LOG.debug('Failed add domain user access, ' + 'attempting to add domain group access. %s', access_info) + if send_rest(access_to, ug_type['adGroup']): + return + + raise exception.InvalidShare(reason=msg) + + def _get_access_from_nfs_share(self, path, clientName): + url = f'rest/nfsShareClient?path={path}&client={clientName}' + + result = self.call(url, None, 'GET') + msg = 'Failed to get share NFS access.' + + self._assert_result_code(result, msg) + share_client = None + if result['data'] is not None: + share_client = {} + share_client['path'] = result['data']['path'] + share_client['clientName'] = result['data']['clientName'] + share_client['accessRight'] = result['data']['accessRight'] + + return share_client + + def _get_access_from_cifs_share(self, share_path, access_to, + ug_input_type=None): + + ug_type = { + 'localUser': '0', + 'localGroup': '1', + 'adUser': '2', + 'adGroup': '3', + } + + msg = 'Failed to get share cifs access.' + access_info = (f'Access info (access_to: {access_to},' + f'path: {share_path}.)') + + def send_rest(access_to, ug_type): + url = f'rest/cifsShareClient?path={share_path}' \ + f'&ugName={access_to}&ugType={ug_type}' + result = self.call(url, None, 'GET') + self._assert_result_code(result, msg) + return result + + share_client = None + if ug_input_type is not None: + ret = send_rest(access_to, ug_input_type) + if ret['data']: + share_client = {} + share_client['path'] = ret['data']['path'] + share_client['ugName'] = ret['data']['ugName'] + share_client['ugType'] = ret['data']['ugType'] + share_client['accessRight'] = ret['data']['accessRight'] + + return share_client + elif '/' not in access_to: + LOG.debug('Attempting to get local user access. %s', access_info) + user_ret = send_rest(access_to, ug_type['localUser']) + if user_ret['code'] == constants.CODE_NOT_FOUND: + return share_client + if user_ret['data']: + share_client = {} + share_client['path'] = user_ret['data']['path'] + share_client['ugName'] = user_ret['data']['ugName'] + share_client['ugType'] = user_ret['data']['ugType'] + share_client['accessRight'] = user_ret['data']['accessRight'] + return share_client + + LOG.debug('Failed get local user access,' + ' attempting to get local group access. %s', access_info) + group_ret = send_rest(access_to, ug_type['localGroup']) + if group_ret['data']: + share_client = {} + share_client['path'] = group_ret['data']['path'] + share_client['ugName'] = group_ret['data']['ugName'] + share_client['ugType'] = group_ret['data']['ugType'] + share_client['accessRight'] = group_ret['data']['accessRight'] + return share_client + else: + str = access_to.index('/') + access_to = access_to[str + 1:] + LOG.debug('Attempting to get domain user access. %s', access_info) + aduser_ret = send_rest(access_to, ug_type['adUser']) + if aduser_ret['code'] == constants.CODE_NOT_FOUND: + return share_client + if aduser_ret['data']: + share_client = {} + share_client['path'] = aduser_ret['data']['path'] + share_client['ugName'] = aduser_ret['data']['ugName'] + share_client['ugType'] = aduser_ret['data']['ugType'] + share_client['accessRight'] = \ + aduser_ret['data']['accessRight'] + return share_client + + LOG.debug('Failed get domain user access,' + ' attempting to get domain group access. %s', + access_info) + adgroup_ret = send_rest(access_to, ug_type['adGroup']) + if adgroup_ret['data']: + share_client = {} + share_client['path'] = adgroup_ret['data']['path'] + share_client['ugName'] = adgroup_ret['data']['ugName'] + share_client['ugType'] = adgroup_ret['data']['ugType'] + share_client['accessRight'] = \ + adgroup_ret['data']['accessRight'] + return share_client + + return share_client + + def _get_all_nfs_access_rest(self, share_path): + url = f'rest/allNfsShareClient?path={share_path}' + + result = self.call(url, None, 'GET') + + msg = 'Get all nfs access error.' + self._assert_result_code(result, msg) + access_list = [] + if result['data'] is None: + pass + else: + for item in result.get('data', []): + access = {} + access['share_path'] = item['path'] + access['access_to'] = item['clientName'] + access['access_level'] = item['accessRight'] + access_list.append(access) + + return access_list + + def _get_all_cifs_access_rest(self, share_path): + url = f'rest/allCifsShareClient?path={share_path}' + + result = self.call(url, None, 'GET') + + msg = 'Get all cifs access error.' + self._assert_result_code(result, msg) + access_list = [] + for item in result.get('data', []): + access = {} + access['share_path'] = item['path'] + access['access_to'] = item['ugName'] + access['ugType'] = item['ugType'] + access['access_level'] = item['accessRight'] + access_list.append(access) + + return access_list + + def _change_nfs_access_rest(self, share_path, access_to, access_level): + url = 'rest/nfsShareClient' + access_info = { + 'path': share_path, + 'oldNfsClientName': access_to, + 'clientName': '', + 'accessRight': access_level, + 'allSquash': '', + 'rootSquash': '', + 'secure': '', + 'anonuid': '', + 'anongid': '', + } + result = self.call(url, access_info, 'PUT') + + msg = 'Update nfs acess error.' + self._assert_result_code(result, msg) + + def _change_cifs_access_rest(self, share_path, access_to, + access_level, ug_type): + url = 'rest/cifsShareClient' + if '/' in access_to: + str = access_to.index('/') + access_to = access_to[str + 1:] + access_info = { + 'path': share_path, + 'right': access_level, + 'ugName': access_to, + 'ugType': ug_type, + } + + result = self.call(url, access_info, 'PUT') + msg = 'Update cifs access error.' + + self._assert_result_code(result, msg) + + def _delete_nfs_access_rest(self, share_path, access_to): + url = f'rest/nfsShareClient?path={share_path}&client={access_to}' + + result = self.call(url, None, 'DELETE') + msg = 'Delete nfs access error.' + + self._assert_result_code(result, msg) + + def _delete_cifs_access_rest(self, share_path, access_to, ug_type): + url = f'rest/cifsShareClient?path={share_path}&ugName={access_to}' \ + f'&ugType={ug_type}' + + result = self.call(url, None, 'DELETE') + msg = 'Delete cifs access error.' + + self._assert_result_code(result, msg) + + def _get_nfs_service_status(self): + url = 'rest/nfsService' + result = self.call(url, None, 'GET') + + msg = 'Get NFS service stauts error.' + self._assert_result_code(result, msg) + + nfs_service = {} + + nfs_service['serviceStatus'] = result['data']['serviceStatus'] + nfs_service['nfs3Status'] = result['data']['nfs3Status'] + nfs_service['nfs4Status'] = result['data']['nfs4Status'] + + return nfs_service + + def _start_nfs_service(self): + url = 'rest/nfsService' + nfs_service_info = { + "openStatus": "1", + } + + result = self.call(url, nfs_service_info, 'PUT') + + self._assert_result_code(result, 'Start NFS service error.') + + def _config_nfs_service(self): + url = 'rest/nfsConfig' + config_nfs = { + 'configNfs3': "yes", + 'configNfs4': "yes", + } + + result = self.call(url, config_nfs, 'PUT') + + self._assert_result_code(result, 'Config NFS service error.') + + def _get_cifs_service_status(self): + url = 'rest/cifsService' + result = self.call(url, None, 'GET') + + msg = 'Get CIFS service status error.' + self._assert_result_code(result, msg) + + return result['data'] + + def _start_cifs_service(self): + url = 'rest/cifsService' + cifs_service_info = { + 'openStatus': '1', + } + + result = self.call(url, cifs_service_info, 'PUT') + + self._assert_result_code(result, 'Start CIFS service error.') + + def _config_cifs_service(self): + url = 'rest/cifsConfig' + """config user mode""" + config_cifs = { + 'workName': 'manila', + 'description': '', + 'access_way': 'user', + 'isCache': 'no', + 'adsName': '', + 'adsIP': '', + 'adsUSER': '', + 'adsPASSWD': '', + 'allowList': [], + 'denyList': [], + } + + result = self.call(url, config_cifs, 'PUT') + + self._assert_result_code(result, 'Config CIFS service error.') + + def _get_all_pool(self): + url = 'rest/storagepool' + + result = self.call(url, None, 'GET') + + msg = 'Query pool info error.' + self._assert_result_code(result, msg) + + return result + + def _query_user(self, user_name): + url = f'rest/user/{user_name}' + + result = self.call(url, None, 'GET') + + msg = 'Query user error.' + self._assert_result_code(result, msg) + return result['data'] + + def _add_localuser(self, user_name, user_passwd, group_name): + url = 'rest/localUser' + user_info = { + 'userName': user_name, + 'mgGroup': group_name, + 'userPasswd': user_passwd, + 'unusedGroup': [], + } + result = self.call(url, user_info, 'POST') + msg = 'add localuser error.' + self._assert_result_code(result, msg) + + def _query_group(self, group_name): + url = f'rest/group/{group_name}' + + result = self.call(url, None, 'GET') + msg = 'Query group error.' + self._assert_result_code(result, msg) + return result['data'] + + def _add_localgroup(self, group_name): + url = 'rest/localGroup' + group_info = { + 'groupName': group_name, + } + result = self.call(url, group_info, 'POST') + + msg = 'add localgroup error.' + self._assert_result_code(result, msg) diff --git a/manila/tests/share/drivers/dell_emc/plugins/unity/mocked_unity.yaml b/manila/tests/share/drivers/dell_emc/plugins/unity/mocked_unity.yaml index 695ab9e518..b57e77c1d3 100644 --- a/manila/tests/share/drivers/dell_emc/plugins/unity/mocked_unity.yaml +++ b/manila/tests/share/drivers/dell_emc/plugins/unity/mocked_unity.yaml @@ -992,6 +992,7 @@ test_create_nfs_share_batch: nfs_share: &nfs_share__test_create_nfs_share_batch _properties: name: '716100cc-e0b4-416b-ac27-d38dd019330d' + size: 151081080 unity: _methods: @@ -1005,11 +1006,6 @@ test_create_nfs_share_batch: _properties: <<: *nas_server_prop - nfs_share: - _properties: - name: '716100cc-e0b4-416b-ac27-d38dd019330d' - size: 151081080 - test_get_share_with_invalid_proto: share: _properties: diff --git a/manila/tests/share/drivers/macrosan/test_macrosan_nas.py b/manila/tests/share/drivers/macrosan/test_macrosan_nas.py index 2fcc9a9ceb..66bbe4b666 100644 --- a/manila/tests/share/drivers/macrosan/test_macrosan_nas.py +++ b/manila/tests/share/drivers/macrosan/test_macrosan_nas.py @@ -1,2250 +1,2250 @@ -# Copyright (c) 2022 MacroSAN Technologies Co., Ltd. -# 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. - -""" -Share driver test for Macrosan Storage Array. -""" -import ddt -import requests - -from oslo_config import cfg -from unittest import mock - -from manila import context -from manila import exception -from manila.share import configuration -from manila.share import driver -from manila.share.drivers.macrosan import macrosan_constants as constants -from manila.share.drivers.macrosan import macrosan_helper -from manila.share.drivers.macrosan import macrosan_nas -from manila.share.drivers.macrosan import rest_helper -from manila import test -from manila.tests import fake_share - -CONF = cfg.CONF - - -class FakeResponse(object): - def __init__(self, status, result): - self.status_code = status - self.text = 'return message' - self.response = result - - def json(self): - return self.response - - def close(self): - pass - - -@ddt.ddt -class MacrosanShareDriverTestCase(test.TestCase): - - def setUp(self): - self.mock_object(macrosan_nas.CONF, '_check_required_opts') - super(MacrosanShareDriverTestCase, self).setUp() - - def _safe_get(opt): - return getattr(self.configuration, opt) - - self._context = context.get_admin_context() - self.configuration = mock.Mock(spec=configuration.Configuration) - self.configuration.safe_get = mock.Mock(side_effect=_safe_get) - self.configuration.driver_handles_share_servers = False - self.configuration.share_backend_name = 'fake_share_backend_name' - self.configuration.macrosan_nas_http_protocol = 'https' - self.configuration.macrosan_nas_ip = 'fake_ip' - self.configuration.macrosan_nas_port = 'fake_port' - self.configuration.macrosan_nas_username = 'fake_username' - self.configuration.macrosan_nas_password = 'fake_password' - self.configuration.macrosan_nas_prefix = 'nas' - self.configuration.macrosan_share_pools = ['fake_pool'] - self.configuration.macrosan_timeout = 60 - self.configuration.macrosan_ssl_cert_verify = False - - self.configuration.network_config_group = 'fake_network_config_group' - self.configuration.admin_network_config_group = ( - 'fake_admin_network_config_group') - self.configuration.config_group = 'fake_config_group' - self.configuration.reserved_share_percentage = 0 - self.configuration.reserved_share_from_snapshot_percentage = 0 - self.configuration.reserved_share_extend_percentage = 0 - self.configuration.filter_function = None - self.configuration.goodness_function = None - self.driver = macrosan_nas.MacrosanNasDriver( - configuration=self.configuration) - self.result_success_storage_pools = { - 'code': 0, - 'message': 'success', - 'data': [{ - 'name': 'fake_pool', - 'size': '1000.0G', - 'allocated': '100G', - 'free': '900G', - 'health': 'ONLINE', - 'rwStatus': 'off' - }] - } - - def test_do_setup(self): - mock_login = self.mock_object(rest_helper.RestHelper, 'login') - self.driver.do_setup(self._context) - mock_login.assert_called_once() - - def test_do_setup_login_fail(self): - mock_login = self.mock_object( - rest_helper.RestHelper, 'login', - mock.Mock( - side_effect=exception.ShareBackendException( - msg='fake_exception'))) - self.assertRaises(exception.ShareBackendException, - self.driver.do_setup, - self._context) - mock_login.assert_called_once() - - @ddt.data({'nfs_status': constants.NFS_NON_CONFIG, - 'cifs_status': constants.CIFS_NON_CONFIG}, - {'nfs_status': constants.NFS_DISABLED, - 'cifs_status': constants.CIFS_DISABLED}, - {'nfs_status': constants.NFS_ENABLED, - 'cifs_status': constants.CIFS_ENABLED}, - {'nfs_status': constants.NFS_ENABLED, - 'cifs_status': constants.CIFS_SHARE_MODE}) - @ddt.unpack - def test_check_for_setup_error_non_config(self, nfs_status, cifs_status): - mock_gnss = self.mock_object( - rest_helper.RestHelper, '_get_nfs_service_status', - mock.Mock(return_value={ - "serviceStatus": nfs_status, - "nfs3Status": constants.NFS_NON_SUPPORTED, - "nfs4Status": constants.NFS_NON_SUPPORTED - })) - - mock_cns = self.mock_object(rest_helper.RestHelper, - '_config_nfs_service') - mock_sns = self.mock_object(rest_helper.RestHelper, - '_start_nfs_service') - if cifs_status == constants.CIFS_DISABLED: - mock_gcss = self.mock_object( - rest_helper.RestHelper, '_get_cifs_service_status', - mock.Mock(side_effect=[cifs_status, - constants.CIFS_SHARE_MODE])) - else: - mock_gcss = self.mock_object( - rest_helper.RestHelper, '_get_cifs_service_status', - mock.Mock(return_value=cifs_status)) - mock_ccs = self.mock_object(rest_helper.RestHelper, - '_config_cifs_service') - mock_scs = self.mock_object(rest_helper.RestHelper, - '_start_cifs_service') - self.driver.check_for_setup_error() - if (nfs_status == constants.NFS_NON_CONFIG or - nfs_status == constants.NFS_DISABLED): - mock_cns.assert_called_once() - mock_sns.assert_called_once() - else: - mock_cns.assert_called_once() - mock_gnss.assert_called_once() - if cifs_status == constants.CIFS_NON_CONFIG: - mock_gcss.assert_called_once() - mock_ccs.assert_called_once() - mock_scs.assert_called_once() - elif cifs_status == constants.CIFS_DISABLED: - mock_gcss.assert_called() - mock_ccs.assert_called_once() - mock_scs.assert_called_once() - elif cifs_status == constants.CIFS_SHARE_MODE: - mock_gcss.assert_called_once() - mock_ccs.assert_called_once() - else: - mock_gcss.assert_called_once() - - def test_check_for_setup_error_nfs_service_error(self): - mock_gnss = self.mock_object( - rest_helper.RestHelper, '_get_nfs_service_status', - mock.Mock(return_value={ - "serviceStatus": constants.NFS_EXCEPTION, - "nfs3Status": constants.NFS_NON_SUPPORTED, - "nfs4Status": constants.NFS_NON_SUPPORTED - })) - self.assertRaises(exception.MacrosanBackendExeption, - self.driver.check_for_setup_error) - mock_gnss.assert_called_once() - - def test_check_for_setup_error_cifs_service_error(self): - mock_gnss = self.mock_object( - rest_helper.RestHelper, '_get_nfs_service_status', - mock.Mock(return_value={ - "serviceStatus": constants.NFS_ENABLED, - "nfs3Status": constants.NFS_SUPPORTED, - "nfs4Status": constants.NFS_SUPPORTED - })) - mock_gcss = self.mock_object( - rest_helper.RestHelper, '_get_cifs_service_status', - mock.Mock(return_value=constants.CIFS_EXCEPTION)) - self.assertRaises(exception.MacrosanBackendExeption, - self.driver.check_for_setup_error) - mock_gnss.assert_called_once() - mock_gcss.assert_called_once() - - @ddt.data('nfs', 'cifs') - def test_create_share(self, share_proto): - share = fake_share.fake_share( - share_proto=share_proto, host="fake_host@fake_backend#fake_pool") - mock_cf = self.mock_object(rest_helper.RestHelper, - '_create_filesystem') - mock_cfd = self.mock_object(rest_helper.RestHelper, - '_create_filesystem_dir') - mock_cns = self.mock_object(rest_helper.RestHelper, - '_create_nfs_share') - self.mock_object(macrosan_helper.MacrosanHelper, - '_ensure_user', - mock.Mock(return_value=True)) - mock_ccs = self.mock_object(rest_helper.RestHelper, - '_create_cifs_share') - self.driver.helper.configuration.macrosan_nas_ip = "172.0.0.1" - - location = self.driver.create_share(self._context, share) - if share_proto == 'nfs': - expect_location = r'172.0.0.1:/manila_fakeid/manila_fakeid' - print('test location:', location) - self.assertEqual(location, expect_location) - else: - expect_location = r'\\172.0.0.1\manila_fakeid' - self.assertEqual(location, expect_location) - mock_cf.assert_called_once_with(fs_name='manila_fakeid', - pool_name='fake_pool', - filesystem_quota='1GB') - mock_cf.assert_called() - share_path = self.driver.helper._generate_share_path('manila_fakeid') - mock_cfd.assert_called_once_with(share_path) - if share_proto == 'nfs': - mock_cns.assert_called_once_with(share_path=share_path) - else: - mock_ccs.assert_called_once() - - def test_create_share_user_error(self): - share = fake_share.fake_share( - share_proto='cifs', host="fake_host@fake_backend#fake_pool") - mock_cf = self.mock_object(rest_helper.RestHelper, - '_create_filesystem') - mock_cfd = self.mock_object(rest_helper.RestHelper, - '_create_filesystem_dir') - self.mock_object(macrosan_helper.MacrosanHelper, - '_ensure_user', - mock.Mock(return_value=False)) - mock_df = self.mock_object(rest_helper.RestHelper, - '_delete_filesystem') - self.assertRaises(exception.MacrosanBackendExeption, - self.driver.create_share, - self._context, - share) - mock_cf.assert_called_once() - share_path = self.driver.helper._generate_share_path('manila_fakeid') - mock_cfd.assert_called_once_with(share_path) - mock_df.assert_called_once_with('manila_fakeid') - - @ddt.data('nfs', 'cifs') - def test_delete_share(self, share_proto): - share = fake_share.fake_share( - share_proto=share_proto, host="fake_host@fake_backend#fake_pool") - expect_share_path = self.driver.helper._generate_share_path( - 'manila_fakeid') - - mock_gns = self.mock_object(rest_helper.RestHelper, '_get_nfs_share', - mock.Mock(return_value={ - "path": "fake_path", - "clients": ["client"], - "protocol": "fake_protocol" - })) - mock_dns = self.mock_object( - rest_helper.RestHelper, '_delete_nfs_share') - mock_gcs = self.mock_object(rest_helper.RestHelper, '_get_cifs_share', - mock.Mock(return_value={ - "path": "fake_path", - "cifsname": "fake_cifsname", - "protocol": "fake_protocol", - "roList": ["fake_ro"], - "rwList": ["fake_rw"], - "allowList": ["fake_allow"], - "denyList": ["fake_deny"] - })) - mock_dcs = self.mock_object(rest_helper.RestHelper, - '_delete_cifs_share') - mock_df = self.mock_object(rest_helper.RestHelper, - '_delete_filesystem') - self.driver.delete_share(self._context, share) - - if share_proto == "nfs": - mock_gns.assert_called_once_with(expect_share_path) - mock_dns.assert_called_once_with(expect_share_path) - else: - mock_gcs.assert_called_once_with(expect_share_path) - mock_dcs.assert_called_once_with('manila_fakeid', - expect_share_path) - mock_df.assert_called_once_with('manila_fakeid') - - @ddt.data('nfs', 'cifs') - def test_delete_share_not_exist(self, share_proto): - share = fake_share.fake_share(share_proto=share_proto, - host="fake_host@fake_backend#fake_pool") - expect_share_path = self.driver.helper._generate_share_path( - 'manila_fakeid') - mock_gns = self.mock_object(rest_helper.RestHelper, '_get_nfs_share', - mock.Mock(return_value=None)) - mock_gf = self.mock_object(rest_helper.RestHelper, '_get_filesystem', - mock.Mock(return_value={ - "name": "fake_name", - "poolName": "fake_pool", - "quotaStatus": "1GB" - })) - mock_gcs = self.mock_object(rest_helper.RestHelper, '_get_cifs_share', - mock.Mock(return_value=None)) - mock_df = self.mock_object(rest_helper.RestHelper, - '_delete_filesystem') - self.driver.delete_share(self._context, share) - - if share_proto == 'nfs': - mock_gns.assert_called_once_with(expect_share_path) - else: - mock_gcs.assert_called_once_with(expect_share_path) - - mock_gf.assert_called_once_with('manila_fakeid') - mock_df.assert_called_once_with('manila_fakeid') - - @ddt.data('nfs', 'cifs') - def test_extend_share(self, share_proto): - share = fake_share.fake_share(share_proto=share_proto, - host="fake_host@fake_backend#fake_pool") - expect_share_path = self.driver.helper._generate_share_path( - 'manila_fakeid') - mock_gns = self.mock_object(rest_helper.RestHelper, '_get_nfs_share', - mock.Mock(return_value={ - "path": "fake_path", - "clients": ["client"], - "protocol": "fake_protocol" - })) - mock_gcs = self.mock_object(rest_helper.RestHelper, '_get_cifs_share', - mock.Mock(return_value={ - "path": "fake_path", - "cifsname": "fake_cifsname", - "protocol": "fake_protocol", - "roList": ["fake_ro"], - "rwList": ["fake_rw"], - "allowList": ["fake_allow"], - "denyList": ["fake_deny"] - })) - mock_uss = self.mock_object(rest_helper.RestHelper, - '_update_share_size') - self.driver.extend_share(share, 2) - - if share_proto == 'nfs': - mock_gns.assert_called_once_with(expect_share_path) - else: - mock_gcs.assert_called_once_with(expect_share_path) - - mock_uss.assert_called_once_with('manila_fakeid', '2GB') - - def test_extend_share_not_exist(self): - share = fake_share.fake_share(share_proto='nfs', - size=1, - host="fake_host@fake_backend#fake_pool") - expect_share_path = self.driver.helper._generate_share_path( - 'manila_fakeid') - mock_gns = self.mock_object(rest_helper.RestHelper, - '_get_nfs_share', - mock.Mock(return_value=None)) - self.assertRaises(exception.ShareResourceNotFound, - self.driver.extend_share, - share, - 2) - - mock_gns.assert_called_once_with(expect_share_path) - - @ddt.data('nfs', 'cifs') - def test_shrink_share(self, share_proto): - share = fake_share.fake_share(share_proto=share_proto, - size=5, - host="fake_host@fake_backend#fake_pool") - expect_share_path = self.driver.helper._generate_share_path( - 'manila_fakeid') - mock_gns = self.mock_object(rest_helper.RestHelper, - '_get_nfs_share', - mock.Mock(return_value={ - "path": "fake_path", - "clients": ["client"], - "protocol": "fake_protocol" - })) - mock_gcs = self.mock_object(rest_helper.RestHelper, '_get_cifs_share', - mock.Mock(return_value={ - "path": "fake_path", - "cifsname": "fake_cifsname", - "protocol": "fake_protocol", - "roList": ["fake_ro"], - "rwList": ["fake_rw"], - "allowList": ["fake_allow"], - "denyList": ["fake_deny"] - })) - mock_gf = self.mock_object(rest_helper.RestHelper, '_get_filesystem', - mock.Mock(return_value={ - "name": "fake_name", - "poolName": "fake_pool", - "quotaStatus": "5GB", - "usedCapacity": '1GB' - })) - mock_uss = self.mock_object(rest_helper.RestHelper, - '_update_share_size') - self.driver.shrink_share(share, 3) - if share_proto == 'nfs': - mock_gns.assert_called_once_with(expect_share_path) - else: - mock_gcs.assert_called_once_with(expect_share_path) - - mock_gf.assert_called_once_with('manila_fakeid') - mock_uss.assert_called_once_with('manila_fakeid', '3GB') - - @ddt.data('nfs', 'cifs') - def test_shrink_share_not_exist(self, share_proto): - share = fake_share.fake_share(share_proto=share_proto, - size=3, - host="fake_host@fake_backend#fake_pool") - expect_share_path = self.driver.helper._generate_share_path( - 'manila_fakeid') - mock_gns = self.mock_object(rest_helper.RestHelper, '_get_nfs_share', - mock.Mock(return_value=None)) - mock_gcs = self.mock_object(rest_helper.RestHelper, '_get_cifs_share', - mock.Mock(return_value=None)) - - self.assertRaises(exception.ShareResourceNotFound, - self.driver.shrink_share, - share, - 1) - if share_proto == 'nfs': - mock_gns.assert_called_once_with(expect_share_path) - elif share_proto == 'cifs': - mock_gcs.assert_called_once_with(expect_share_path) - - def test_shrink_share_size_fail(self): - share = fake_share.fake_share(share_proto='nfs', - size=3, - host="fake_host@fake_backend#fake_pool") - expect_share_path = self.driver.helper._generate_share_path( - 'manila_fakeid') - mock_gns = self.mock_object(rest_helper.RestHelper, - '_get_nfs_share', - mock.Mock(return_value={ - "path": "fake_path", - "clients": ["client"], - "protocol": "fake_protocol" - })) - mock_gf = self.mock_object(rest_helper.RestHelper, '_get_filesystem', - mock.Mock(return_value={ - "name": "fake_name", - "poolName": "fake_pool", - "quotaStatus": "3GB", - "usedCapacity": '2GB' - })) - self.assertRaises(exception.ShareShrinkingPossibleDataLoss, - self.driver.shrink_share, - share, - 1) - mock_gf.assert_called_once_with('manila_fakeid') - mock_gns.assert_called_once_with(expect_share_path) - - @ddt.data('nfs', 'cifs') - def test_ensure_share(self, share_proto): - share = fake_share.fake_share(share_proto=share_proto, - host="fake_host@fake_backend#fake_pool") - mock_gns = self.mock_object(rest_helper.RestHelper, '_get_nfs_share', - mock.Mock(return_value={ - "path": "fake_path", - "clients": ["client"], - "protocol": "fake_protocol" - })) - mock_gcs = self.mock_object(rest_helper.RestHelper, '_get_cifs_share', - mock.Mock(return_value={ - "path": "fake_path", - "cifsname": "fake_cifsname", - "protocol": "fake_protocol", - "roList": ["fake_ro"], - "rwList": ["fake_rw"], - "allowList": ["fake_allow"], - "denyList": ["fake_deny"], - })) - self.driver.helper.configuration.macrosan_nas_ip = "172.0.0.1" - locations = self.driver.ensure_share(self._context, share) - expect_share_path = self.driver.helper._generate_share_path( - 'manila_fakeid') - if share_proto == 'nfs': - expect_locations = [r'172.0.0.1:/manila_fakeid/manila_fakeid'] - self.assertEqual(locations, expect_locations) - mock_gns.assert_called_once_with(expect_share_path) - else: - expect_locations = [r'\\172.0.0.1\manila_fakeid'] - self.assertEqual(locations, expect_locations) - mock_gcs.assert_called_once_with(expect_share_path) - - def test_ensure_share_proto_fail(self): - share = fake_share.fake_share(host="fake_host@fake_backend#fake_pool") - self.mock_object(rest_helper.RestHelper, '_get_nfs_share', - mock.Mock(return_value={ - "path": "fake_path", - "clients": ["client"], - "protocol": "fake_protocol" - })) - self.assertRaises(exception.MacrosanBackendExeption, - self.driver.ensure_share, - self._context, - share) - - def test_ensure_share_not_exist(self): - share = fake_share.fake_share(share_proto='nfs', - host="fake_host@fake_backend#fake_pool") - mock_gns = self.mock_object(rest_helper.RestHelper, '_get_nfs_share', - mock.Mock(return_value=None)) - self.assertRaises(exception.ShareResourceNotFound, - self.driver.ensure_share, - self._context, - share) - expect_share_path = self.driver.helper._generate_share_path( - 'manila_fakeid') - mock_gns.assert_called_once_with(expect_share_path) - - @ddt.data('nfs', 'cifs') - def test_allow_access_success(self, share_proto): - share = fake_share.fake_share(share_proto=share_proto, - host="fake_host@fake_backend#fake_pool") - if share_proto == 'nfs': - access = { - 'access_type': 'ip', - 'access_to': '0.0.0.0/0', - 'access_level': 'rw', - } - else: - access = { - 'access_type': 'user', - 'access_to': 'fake_user', - 'access_level': 'rw', - } - mock_gns = self.mock_object(rest_helper.RestHelper, - '_get_nfs_share', - mock.Mock(return_value={ - "path": "fake_path", - "clients": ["client"], - "protocol": "fake_protocol" - })) - mock_gafns = self.mock_object(rest_helper.RestHelper, - '_get_access_from_nfs_share', - mock.Mock(return_value=None)) - mock_anar = self.mock_object(rest_helper.RestHelper, - '_allow_nfs_access_rest') - mock_gcs = self.mock_object(rest_helper.RestHelper, - '_get_cifs_share', - mock.Mock(return_value={ - "path": "fake_path", - "cifsname": "fake_cifsname", - "protocol": "fake_protocol", - "roList": ["fake_ro"], - "rwList": ["fake_rw"], - "allowList": ["fake_allow"], - "denyList": ["fake_deny"], - })) - mock_gafcs = self.mock_object(rest_helper.RestHelper, - '_get_access_from_cifs_share', - mock.Mock(return_value=None)) - mock_acar = self.mock_object(rest_helper.RestHelper, - '_allow_cifs_access_rest') - self.driver.helper._allow_access(share, access) - expect_share_path = self.driver.helper._generate_share_path( - 'manila_fakeid') - if access['access_to'] == '0.0.0.0/0': - access['access_to'] = '*' - if share_proto == 'nfs': - mock_gns.assert_called_once_with(expect_share_path) - mock_gafns.assert_called_once_with(expect_share_path, - access['access_to']) - mock_anar.assert_called_once_with(expect_share_path, - access['access_to'], - access['access_level']) - else: - mock_gcs.assert_called_once_with(expect_share_path) - mock_gafcs.assert_called_once_with(expect_share_path, - access['access_to']) - mock_acar.assert_called_once_with(expect_share_path, - access['access_to'], - access['access_level']) - - def test_allow_access_nfs_change(self): - share = fake_share.fake_share(share_proto='nfs', - host="fake_host@fake_backend#fake_pool") - access = { - 'access_type': 'ip', - 'access_to': '172.0.0.1', - 'access_level': 'rw', - } - mock_gns = self.mock_object(rest_helper.RestHelper, '_get_nfs_share', - mock.Mock(return_value={ - "path": "/manila_fakeid", - "clients": ["client"], - "protocol": "fake_protocol" - })) - mock_gafns = self.mock_object(rest_helper.RestHelper, - '_get_access_from_nfs_share', - mock.Mock(return_value={ - "path": "/manila_fakeid", - "clientName": "fake_client_name", - "accessRight": "ro", - })) - mock_cnar = self.mock_object(rest_helper.RestHelper, - '_change_nfs_access_rest') - self.driver.helper._allow_access(share, access) - expect_share_path = self.driver.helper._generate_share_path( - 'manila_fakeid') - mock_gns.assert_called_once_with(expect_share_path) - mock_gafns.assert_called_once_with(expect_share_path, - access['access_to']) - mock_cnar.assert_called_once_with(expect_share_path, - access['access_to'], - access['access_level']) - - def test_allow_access_cifs_change(self): - share = fake_share.fake_share(share_proto='cifs', - host="fake_host@fake_backend#fake_pool") - access = { - 'access_type': 'user', - 'access_to': 'fake_user', - 'access_level': 'rw', - } - mock_gcs = self.mock_object(rest_helper.RestHelper, - '_get_cifs_share', - mock.Mock(return_value={ - "path": "fake_path", - "cifsname": "fake_cifsname", - "protocol": "fake_protocol", - "roList": ["fake_ro"], - "rwList": ["fake_rw"], - "allowList": ["fake_allow"], - "denyList": ["fake_deny"], - })) - mock_gafcs = self.mock_object(rest_helper.RestHelper, - '_get_access_from_cifs_share', - mock.Mock(return_value={ - "path": "fake_path", - "ugName": "fake_user", - "ugType": "0", - "accessRight": "ro", - })) - mock_ccar = self.mock_object(rest_helper.RestHelper, - '_change_cifs_access_rest') - self.driver.helper._allow_access(share, access) - expect_share_path = self.driver.helper._generate_share_path( - 'manila_fakeid') - mock_gcs.assert_called_once_with(expect_share_path) - mock_gafcs.assert_called_once_with(expect_share_path, - access['access_to']) - mock_ccar.assert_called_once_with(expect_share_path, - access['access_to'], - access['access_level'], - '0') - - @ddt.data( - { - 'access_type': 'user', - 'access_to': 'user_name', - 'access_level': 'rw', - }, - { - 'access_type': 'user', - 'access_to': 'group_name', - 'access_level': 'rw', - }, - { - 'access_type': 'user', - 'access_to': '/domain_user', - 'access_level': 'rw', - }, - { - 'access_type': 'user', - 'access_to': '/domain_group', - 'access_level': 'rw', - }, - ) - def test_allow_access_cifs(self, access): - share = fake_share.fake_share(share_proto='cifs', - host="fake_host@fake_backend#fake_pool") - mock_gcs = self.mock_object(rest_helper.RestHelper, - '_get_cifs_share', - mock.Mock(return_value={ - "path": "fake_path", - "cifsname": "fake_cifsname", - "protocol": "fake_protocol", - "roList": ["fake_ro"], - "rwList": ["fake_rw"], - "allowList": ["fake_allow"], - "denyList": ["fake_deny"], - })) - mock_gafcs = self.mock_object(rest_helper.RestHelper, - '_get_access_from_cifs_share', - mock.Mock(return_value=None)) - mock_acar = self.mock_object(rest_helper.RestHelper, - '_allow_cifs_access_rest') - - self.driver.helper._allow_access(share, access) - expect_share_path = self.driver.helper._generate_share_path( - 'manila_fakeid') - mock_gcs.assert_called_once_with(expect_share_path) - mock_gafcs.assert_called_once_with(expect_share_path, - access['access_to']) - mock_acar.assert_called_once_with(expect_share_path, - access['access_to'], - access['access_level']) - - @ddt.data('nfs', 'cifs') - def test_allow_access_share_not_exist(self, share_proto): - share = fake_share.fake_share(share_proto=share_proto, - host="fake_host@fake_backend#fake_pool") - access = {} - if share_proto == 'nfs': - access = { - 'access_type': 'ip', - 'access_to': '172.0.0.1', - 'access_level': 'rw', - } - else: - access = { - 'access_type': 'user', - 'access_to': 'fake_user', - 'access_level': 'rw', - } - mock_gns = self.mock_object(rest_helper.RestHelper, '_get_nfs_share', - mock.Mock(return_value=None)) - mock_gcs = self.mock_object(rest_helper.RestHelper, '_get_cifs_share', - mock.Mock(return_value=None)) - self.assertRaises(exception.ShareResourceNotFound, - self.driver.helper._allow_access, - share, - access) - expect_share_path = self.driver.helper._generate_share_path( - 'manila_fakeid') - if share_proto == 'nfs': - mock_gns.assert_called_once_with(expect_share_path) - else: - mock_gcs.assert_called_once_with(expect_share_path) - - def test_allow_access_proto_fail(self): - share = fake_share.fake_share(host="fake_host@fake_backend#fake_pool") - access = { - 'access_type': 'user', - 'access_to': 'fake_user', - 'access_level': 'rw', - } - self.assertRaises(exception.MacrosanBackendExeption, - self.driver.helper._allow_access, - share, - access) - - def test_allow_access_nfs_user_fail(self): - share = fake_share.fake_share(share_proto='nfs', - host="fake_host@fake_backend#fake_pool") - access = { - 'access_type': 'user', - 'access_to': 'fake_user', - 'access_level': 'rw', - } - self.assertRaises(exception.InvalidShareAccess, - self.driver.helper._allow_access, - share, - access) - - def test_allow_access_cifs_ip_fail(self): - share = fake_share.fake_share(share_proto='cifs', - host="fake_host@fake_backend#fake_pool") - access = { - 'access_type': 'ip', - 'access_to': '172.0.0.1', - 'access_level': 'rw', - } - self.assertRaises(exception.InvalidShareAccess, - self.driver.helper._allow_access, - share, - access) - - def test_allow_access_nfs_level_fail(self): - share = fake_share.fake_share(share_proto='nfs', - host="fake_host@fake_backend#fake_pool") - access = { - 'access_type': 'ip', - 'access_to': '172.0.0.1', - 'access_level': 'r', - } - self.assertRaises(exception.InvalidShareAccess, - self.driver.helper._allow_access, - share, - access) - - @ddt.data('nfs', 'cifs') - def test_deny_access(self, share_proto): - share = fake_share.fake_share(share_proto=share_proto, - host="fake_host@fake_backend#fake_pool") - if share_proto == 'nfs': - access = { - 'access_type': 'ip', - 'access_to': '0.0.0.0/0', - 'access_level': 'rw', - } - else: - access = { - 'access_type': 'user', - 'access_to': 'fake_user', - 'access_level': 'rw', - } - mock_gafns = self.mock_object(rest_helper.RestHelper, - '_get_access_from_nfs_share', - mock.Mock(return_value={ - "path": "fake_path", - "clientName": "fake_client_name", - "accessRight": "rw", - })) - mock_dnar = self.mock_object(rest_helper.RestHelper, - '_delete_nfs_access_rest') - mock_gafcs = self.mock_object(rest_helper.RestHelper, - '_get_access_from_cifs_share', - mock.Mock(return_value={ - "path": "fake_path", - "ugName": "fake_user", - "ugType": "0", - "accessRight": "rw", - })) - mock_dcar = self.mock_object(rest_helper.RestHelper, - '_delete_cifs_access_rest') - expect_share_path = self.driver.helper._generate_share_path( - 'manila_fakeid') - self.driver.helper._deny_access(share, access) - if access['access_to'] == '0.0.0.0/0': - access['access_to'] = '*' - if share_proto == 'nfs': - mock_gafns.assert_called_once_with(expect_share_path, - access['access_to']) - mock_dnar.assert_called_once_with(expect_share_path, - access['access_to']) - else: - mock_gafcs.assert_called_once_with(expect_share_path, - access['access_to']) - mock_dcar.assert_called_once_with(expect_share_path, - "fake_user", "0") - - def test_deny_access_nfs_type_fail(self): - share = fake_share.fake_share(share_proto='nfs', - host="fake_host@fake_backend#fake_pool") - access = { - 'access_type': 'fake_type', - 'access_to': '172.0.0.1', - 'access_level': 'rw', - } - result = self.driver.helper._deny_access(share, access) - self.assertIsNone(result) - - def test_deny_access_nfs_share_not_exist(self): - share = fake_share.fake_share(share_proto='nfs', - host="fake_host@fake_backend#fake_pool") - access = { - 'access_type': 'ip', - 'access_to': '172.0.0.1', - 'access_level': 'rw', - } - mock_gafns = self.mock_object(rest_helper.RestHelper, - '_get_access_from_nfs_share', - mock.Mock(return_value=None)) - result = self.driver.helper._deny_access(share, access) - self.assertIsNone(result) - expect_share_path = self.driver.helper._generate_share_path( - 'manila_fakeid') - mock_gafns.assert_called_once_with(expect_share_path, - access['access_to']) - - def test_deny_access_cifs_type_fail(self): - share = fake_share.fake_share(share_proto='cifs', - host="fake_host@fake_backend#fake_pool") - access = { - 'access_type': 'fake_type', - 'access_to': 'fake_user', - 'access_level': 'rw', - } - result = self.driver.helper._deny_access(share, access) - self.assertIsNone(result) - - def test_deny_access_cifs_share_not_exist(self): - share = fake_share.fake_share(share_proto='cifs', - host="fake_host@fake_backend#fake_pool") - access = { - 'access_type': 'user', - 'access_to': 'fake_user', - 'access_level': 'rw', - } - mock_gafcs = self.mock_object(rest_helper.RestHelper, - '_get_access_from_cifs_share', - mock.Mock(return_value=None)) - result = self.driver.helper._deny_access(share, access) - self.assertIsNone(result) - expect_share_path = self.driver.helper._generate_share_path( - 'manila_fakeid') - mock_gafcs.assert_called_once_with(expect_share_path, - access['access_to']) - - def test_update_access_add_delete(self): - share = fake_share.fake_share(share_proto='nfs', - host="fake_host@fake_backend#fake_pool") - add_rules = [{'access_type': 'ip', - 'access_to': '172.0.2.1', - 'access_level': 'rw', }] - delete_rules = [{'access_type': 'ip', - 'access_to': '172.0.2.2', - 'access_level': 'rw', }] - self.mock_object(macrosan_helper.MacrosanHelper, - '_allow_access') - self.mock_object(macrosan_helper.MacrosanHelper, - '_deny_access') - self.driver.update_access(self._context, share, - None, add_rules, delete_rules) - - @ddt.data('nfs', 'cifs') - def test_update_access_nfs(self, proto): - share = fake_share.fake_share(share_proto=proto, - host="fake_host@fake_backend#fake_pool") - if proto == 'nfs': - access_rules = [{'access_type': 'ip', - 'access_to': '172.0.3.1', - 'access_level': 'rw', }, - {'access_type': 'ip', - 'access_to': '172.0.3.2', - 'access_level': 'rw', }] - else: - access_rules = [{'access_type': 'user', - 'access_to': 'user_l', - 'access_level': 'rw', }, - {'access_type': 'user', - 'access_to': 'user_a', - 'access_level': 'rw', }] - mock_ca = self.mock_object(macrosan_helper.MacrosanHelper, - '_clear_access') - self.mock_object(macrosan_helper.MacrosanHelper, - '_allow_access') - self.driver.update_access(self._context, share, - access_rules, {}, {}) - mock_ca.assert_called_once_with(share, None) - - def test_update_access_fail(self): - share = fake_share.fake_share(share_proto='nfs', - host="fake_host@fake_backend#fake_pool") - access_rules = [{'access_id': 'fakeid', - 'access_type': 'ip', - 'access_to': '172.0.3.1', - 'access_level': 'rw', }] - mock_ca = self.mock_object(macrosan_helper.MacrosanHelper, - '_clear_access') - self.mock_object(macrosan_helper.MacrosanHelper, - '_allow_access', - mock.Mock(side_effect=exception.InvalidShareAccess( - reason='fake_exception'))) - result = self.driver.update_access(self._context, share, - access_rules, None, None) - expect = { - 'fakeid': { - 'state': 'error', - } - } - self.assertEqual(result, expect) - mock_ca.assert_called_once_with(share, None) - - def test_update_access_add_fail(self): - share = fake_share.fake_share(share_proto='nfs', - host="fake_host@fake_backend#fake_pool") - add_rules = [{'access_id': 'fakeid', - 'access_type': 'ip', - 'access_to': '172.0.2.1', - 'access_level': 'rw', }] - delete_rules = [] - self.mock_object(macrosan_helper.MacrosanHelper, - '_allow_access', - mock.Mock(side_effect=exception.InvalidShareAccess( - reason='fake_exception'))) - self.mock_object(macrosan_helper.MacrosanHelper, - '_deny_access') - result = self.driver.update_access(self._context, share, - None, add_rules, delete_rules) - expect = { - 'fakeid': { - 'state': 'error' - } - } - self.assertEqual(result, expect) - - @ddt.data('nfs', 'cifs') - def test__clear_access(self, share_proto): - share = fake_share.fake_share(share_proto=share_proto, - host="fake_host@fake_backend#fake_pool") - fake_nfs_share_backend = [ - { - 'share_path': 'fake_path', - 'access_to': '172.0.0.1', - 'access_level': 'rw' - }, - { - 'share_path': 'default_path', - 'access_to': '172.0.0.2', - 'access_level': 'rw' - }] - fake_cifs_share_backend = [ - { - 'share_path': 'fake_path', - 'access_to': 'user_name', - 'ugType': '0', - 'access_level': 'rw' - }, - { - 'share_path': 'default_path', - 'access_to': 'manilanobody', - 'ugType': '0', - 'access_level': 'rw' - }] - mock_ganar = self.mock_object( - rest_helper.RestHelper, - '_get_all_nfs_access_rest', - mock.Mock(return_value=fake_nfs_share_backend)) - mock_gacar = self.mock_object( - rest_helper.RestHelper, '_get_all_cifs_access_rest', - mock.Mock(return_value=fake_cifs_share_backend)) - self.mock_object(rest_helper.RestHelper, - '_delete_nfs_access_rest') - self.mock_object(rest_helper.RestHelper, - '_delete_cifs_access_rest') - self.driver.helper._clear_access(share) - expect_share_path = self.driver.helper._generate_share_path( - 'manila_fakeid') - if share_proto == 'nfs': - mock_ganar.assert_called_once_with(expect_share_path) - else: - mock_gacar.assert_called_once_with(expect_share_path) - - @ddt.data('nfs', 'cifs') - def test__clear_access_no_access_list(self, share_proto): - share = fake_share.fake_share(share_proto=share_proto, - host="fake_host@fake_backend#fake_pool") - mock_ganar = self.mock_object( - rest_helper.RestHelper, - '_get_all_nfs_access_rest', - mock.Mock(return_value=[])) - mock_gacar = self.mock_object( - rest_helper.RestHelper, '_get_all_cifs_access_rest', - mock.Mock(return_value=[])) - self.driver.helper._clear_access(share) - expect_share_path = self.driver.helper._generate_share_path( - 'manila_fakeid') - if share_proto == 'nfs': - mock_ganar.assert_called_once_with(expect_share_path) - else: - mock_gacar.assert_called_once_with(expect_share_path) - - @ddt.data(constants.USER_NOT_EXIST, - constants.USER_EXIST, - constants.USER_FORMAT_ERROR) - def test__ensure_user(self, query_result): - mock_qu = self.mock_object(rest_helper.RestHelper, - '_query_user', - mock.Mock(return_value=query_result)) - mock_qg = self.mock_object( - rest_helper.RestHelper, - '_query_group', - mock.Mock(return_value=constants.GROUP_NOT_EXIST)) - mock_alg = self.mock_object(rest_helper.RestHelper, - '_add_localgroup') - mock_alu = self.mock_object(rest_helper.RestHelper, - '_add_localuser') - result = self.driver.helper._ensure_user('fake_user', - 'fake_passwd', - 'fake_group') - if query_result == constants.USER_NOT_EXIST: - mock_qg.assert_called_once_with('fake_group') - mock_alg.assert_called_once_with('fake_group') - mock_alu.assert_called_once_with('fake_user', - 'fake_passwd', - 'fake_group') - self.assertTrue(result) - elif query_result == constants.USER_EXIST: - self.assertTrue(result) - else: - self.assertFalse(result) - mock_qu.assert_called_once_with('fake_user') - - def test__ensure_user_fail(self): - mock_qu = self.mock_object( - rest_helper.RestHelper, - '_query_user', - mock.Mock(return_value=constants.USER_NOT_EXIST)) - mock_qg = self.mock_object( - rest_helper.RestHelper, - '_query_group', - mock.Mock(return_value=constants.GROUP_FORMAT_ERROR)) - self.assertRaises(exception.InvalidInput, - self.driver.helper._ensure_user, - 'fake_user', - 'fake_passwd', - 'fake_group') - mock_qu.assert_called_once_with('fake_user') - mock_qg.assert_called_once_with('fake_group') - - def test__update_share_stats(self): - self.driver.helper.pools = ['fake_pool'] - mock_gap = self.mock_object(rest_helper.RestHelper, - '_get_all_pool', - mock.Mock(return_value='fake_result')) - mock_gpc = self.mock_object(macrosan_helper.MacrosanHelper, - '_get_pool_capacity', - mock.Mock(return_value={ - "totalcapacity": 10, - "freecapacity": 9, - "allocatedcapacity": 1, - })) - mock_uss = self.mock_object(driver.ShareDriver, '_update_share_stats') - - self.driver._update_share_stats() - - data = {} - data['vendor_name'] = self.driver.VENDOR - data['driver_version'] = self.driver.VERSION - data['storage_protocol'] = self.driver.PROTOCOL - data['share_backend_name'] = 'fake_share_backend_name' - data['pools'] = [{ - 'pool_name': 'fake_pool', - 'total_capacity_gb': 10, - 'free_capacity_gb': 9, - 'allocated_capacity_gb': 1, - 'reserved_percentage': 0, - 'reserved_snapshot_percentage': 0, - 'reserved_share_extend_percentage': 0, - 'dedupe': False, - 'compression': False, - 'qos': False, - 'thin_provisioning': False, - 'snapshot_support': False, - 'create_share_from_snapshot_support': - False, - }] - mock_gap.assert_called_once() - mock_gpc.assert_called_once_with('fake_pool', 'fake_result') - mock_uss.assert_called_once_with(data) - - def test__update_share_stats_pool_not_exist(self): - self.driver.helper.pools = ['fake_pool'] - self.mock_object(rest_helper.RestHelper, '_get_all_pool', - mock.Mock(return_value='fake_result')) - self.mock_object(macrosan_helper.MacrosanHelper, - '_get_pool_capacity', - mock.Mock(return_value={})) - self.assertRaises(exception.InvalidInput, - self.driver._update_share_stats - ) - - def test__get_pool_capacity(self): - self.mock_object(macrosan_helper.MacrosanHelper, - '_find_pool_info', - mock.Mock(return_value={ - "name": "fake_pool", - "totalcapacity": "100.0G", - "allocatedcapacity": "22G", - "freecapacity": "78G", - "health": "ONLINE", - "rw": "off", - })) - res = self.driver.helper._get_pool_capacity("fake_pool", - "fake_result") - self.assertEqual(100, res['totalcapacity']) - self.assertEqual(78, res['freecapacity']) - self.assertEqual(22, res['allocatedcapacity']) - - def test__generate_share_name(self): - share = fake_share.fake_share(host="fake_host@fake_backend#fake_pool") - result = self.driver.helper._generate_share_name(share) - self.assertEqual("manila_fakeid", result) - - def test__format_name(self): - a = 'fake-1234567890-1234567890-1234567890' - expect = 'fake_1234567890_1234567890_1234' - result = self.driver.helper._format_name(a) - self.assertEqual(expect, result) - - def test__generate_share_path(self): - share_name = 'manila_fakeid' - result = self.driver.helper._generate_share_path(share_name) - - self.assertEqual(r'/manila_fakeid/manila_fakeid', result) - - @ddt.data('nfs', 'cifs') - def test__get_location_path(self, share_proto): - self.driver.helper.configuration.macrosan_nas_ip = "172.0.0.1" - result = self.driver.helper._get_location_path('fake_path', - 'fake_name', - share_proto) - if share_proto == 'nfs': - expect = r'172.0.0.1:fake_path' - elif share_proto == 'cifs': - expect = r'\\172.0.0.1\fake_name' - self.assertEqual(expect, result) - - def test__get_share_instance_pnp_pool_error(self): - share = fake_share.fake_share( - share_proto="nfs", host="fake_host@fake_backend") - self.assertRaises(exception.InvalidHost, - self.driver.helper._get_share_instance_pnp, - share) - - def test__get_share_instance_pnp_proto_error(self): - share = fake_share.fake_share( - share_proto="CephFS", host="fake_host@fake_backend#fake_pool") - self.assertRaises(exception.MacrosanBackendExeption, - self.driver.helper._get_share_instance_pnp, - share) - - @ddt.data('2000000000', '2000000KB', '2000MB', '20GB', '2TB') - def test__unit_convert_toGB(self, capacity): - convert = {'2000000000': '%.0f' % (float(2000000000) / 1024 ** 3), - '2000000KB': '%.0f' % (float(2000000) / 1024 ** 2), - '2000MB': '%.0f' % (float(2000) / 1024), - '20GB': '%.0f' % float(20), - '2TB': '%.0f' % (float(2) * 1024)} - expect = float(convert[capacity]) - result = self.driver.helper._unit_convert_toGB(capacity) - self.assertEqual(expect, result) - - @ddt.data('nfs', 'cifs') - def test__get_share(self, proto): - proto = proto.upper() - mock_gns = self.mock_object(rest_helper.RestHelper, - '_get_nfs_share', - mock.Mock(return_value={ - "path": "/manila_fakeid", - "clients": ["client"], - "protocol": "NFS" - })) - mock_gcs = self.mock_object(rest_helper.RestHelper, - '_get_cifs_share', - mock.Mock(return_value={ - "path": "fake_path", - "cifsname": "fake_cifsname", - "protocol": "CIFS", - "roList": ["fake_ro"], - "rwList": ["fake_rw"], - "allowList": ["fake_allow"], - "denyList": ["fake_deny"], - })) - expect_nfs = { - "path": "/manila_fakeid", - "clients": ["client"], - "protocol": "NFS"} - expect_cifs = { - "path": "fake_path", - "cifsname": "fake_cifsname", - "protocol": "CIFS", - "roList": ["fake_ro"], - "rwList": ["fake_rw"], - "allowList": ["fake_allow"], - "denyList": ["fake_deny"]} - result = self.driver.helper._get_share('fake_path', proto) - if proto == 'NFS': - mock_gns.assert_called_once_with('fake_path') - self.assertEqual(expect_nfs, result) - elif proto == 'CIFS': - mock_gcs.assert_called_once_with('fake_path') - self.assertEqual(expect_cifs, result) - - def test__find_pool_info(self): - pool_info = self.driver.helper._find_pool_info( - 'fake_pool', - self.result_success_storage_pools) - self.assertIsNotNone(pool_info) - - def test__find_pool_info_fail(self): - pool_info = self.driver.helper._find_pool_info( - 'error_pool', - self.result_success_storage_pools) - expect = {} - self.assertEqual(expect, pool_info) - - -@ddt.ddt -class RestHelperTestCase(test.TestCase): - - def setUp(self): - self.mock_object(CONF, '_check_required_opts') - super(RestHelperTestCase, self).setUp() - - def _safe_get(opt): - return getattr(self.configuration, opt) - - self.configuration = mock.Mock(spec=configuration.Configuration) - self.configuration.safe_get = mock.Mock(side_effect=_safe_get) - self.configuration.macrosan_nas_http_protocol = 'https' - self.configuration.macrosan_nas_ip = 'fake_ip' - self.configuration.macrosan_nas_port = 'fake_port' - self.configuration.macrosan_nas_prefix = 'nas' - self.configuration.macrosan_nas_username = 'fake_username' - self.configuration.macrosan_nas_password = 'fake_password' - self.configuration.macrosan_timeout = 60 - self.configuration.macrosan_ssl_cert_verify = False - self.resthelper = rest_helper.RestHelper( - configuration=self.configuration) - self.post = 'POST' - self.get = 'GET' - self.delete = 'DELETE' - self.put = 'PUT' - self.fake_message = 'fake_message' - self.result_success = { - 'code': 0, - 'message': 'success', - 'data': 'fake_data' - } - self.result_success_return_0 = { - 'code': 0, - 'message': 'success', - 'data': '0' - } - self.result_success_return_1 = { - 'code': 0, - 'message': 'success', - 'data': '1' - } - self.result_failed = { - 'code': 1, - 'message': 'failed', - 'data': 'fake_data' - } - self.result_failed_not_exist = { - 'code': constants.CODE_SOURCE_NOT_EXIST, - 'message': 'failed', - 'data': '', - } - self.result_success_storage_pools = { - 'code': 0, - 'message': 'success', - 'data': [{ - 'name': 'fake_pool', - 'size': '1000.0G', - 'allocated': '100G', - 'free': '900G', - 'health': 'ONLINE', - 'rwStatus': 'off' - }] - } - - @ddt.data( - {'url': 'fake_url', 'data': {'fake_data': 'fake_value'}, - 'method': 'POST'}, - {'url': 'fake_url', 'data': None, - 'method': 'GET'}, - {'url': 'fake_url', 'data': {'fake_data': 'fake_value'}, - 'method': 'DELETE'}, - {'url': 'fake_url', 'data': {'fake_data': 'fake_value'}, - 'method': 'PUT'}, - ) - @ddt.unpack - def test_call(self, url, data, method): - self.resthelper._token = 'fake_token' - request_method = method.lower() - fake_response = FakeResponse(200, self.result_success) - mock_request = self.mock_object(requests, request_method, - mock.Mock(return_value=fake_response)) - self.resthelper.call(url, data, method) - expected_url = ('https://%(ip)s:%(port)s/%(rest)s/%(url)s' - % {'ip': 'fake_ip', - 'port': 'fake_port', - 'rest': 'nas', - 'url': 'fake_url'}) - header = {'Authorization': 'fake_token'} - mock_request.assert_called_once_with( - expected_url, data=data, headers=header, - timeout=self.configuration.macrosan_timeout, - verify=False) - - def test_call_method_fail(self): - self.resthelper._token = 'fake_token' - self.assertRaises(exception.ShareBackendException, - self.resthelper.call, - 'fake_url', - 'fake_data', - 'error_method') - - def test_call_token_fail(self): - self.resthelper._token = 'fake_token' - fake_result_fail = { - 'code': 302, - 'message': 'fake_message', - 'data': 'fake_data' - } - self.mock_object(self.resthelper, 'do_request', - mock.Mock(return_value=fake_result_fail)) - self.assertRaises(exception.MacrosanBackendExeption, - self.resthelper.call, - 'fake_url', - 'fake_data', - self.post) - - def test_call_token_none(self): - self.resthelper._token = None - self.mock_object(self.resthelper, 'do_request', - mock.Mock(return_value=self.result_success)) - mock_l = self.mock_object(self.resthelper, 'login', - mock.Mock(return_value='fake_token')) - self.resthelper.call('fake_url', 'fake_data', self.post) - mock_l.assert_called_once() - - def test_call_token_expired(self): - self.resthelper._token = 'fake_token' - fake_result = { - 'code': 301, - 'message': 'token expired', - 'data': 'fake_data' - } - self.mock_object( - self.resthelper, 'do_request', - mock.Mock(side_effect=[fake_result, self.result_success])) - mock_l = self.mock_object(self.resthelper, 'login', - mock.Mock(return_value='fake_token')) - self.resthelper.call('fake_url', 'fake_data', self.post) - mock_l.assert_called_once() - - def test_call_fail(self): - self.resthelper._token = 'fake_token' - fake_response = FakeResponse(302, self.result_success) - self.mock_object(requests, 'post', - mock.Mock(return_value=fake_response)) - self.assertRaises(exception.NetworkException, - self.resthelper.call, - 'fake_url', - 'fake_data', - self.post) - - def test_login(self): - fake_result = { - 'code': 0, - 'message': 'Login success', - 'data': 'fake_token' - } - mock_rd = self.mock_object(self.resthelper, 'do_request', - mock.Mock(return_value=fake_result)) - self.resthelper.login() - login_data = {'userName': self.configuration.macrosan_nas_username, - 'userPasswd': self.configuration.macrosan_nas_password} - mock_rd.assert_called_once_with('rest/token', login_data, - self.post) - self.assertEqual('fake_token', self.resthelper._token) - - def test_login_fail(self): - mock_rd = self.mock_object(self.resthelper, 'do_request', - mock.Mock(return_value=self.result_failed)) - - self.assertRaises(exception.ShareBackendException, - self.resthelper.login) - login_data = {'userName': self.configuration.macrosan_nas_username, - 'userPasswd': self.configuration.macrosan_nas_password} - mock_rd.assert_called_once_with('rest/token', login_data, - self.post) - - def test__assert_result_code(self): - self.resthelper._assert_result_code(self.result_success, - self.fake_message) - - def test__assert_result_code_fail(self): - self.assertRaises(exception.ShareBackendException, - self.resthelper._assert_result_code, - self.result_failed, - self.fake_message) - - def test__assert_result_data(self): - self.resthelper._assert_result_data(self.result_success, - self.fake_message) - - def test__assert_result_data_fail(self): - fake_result = { - 'code': 0, - 'message': 'fake_message' - } - self.assertRaises(exception.ShareBackendException, - self.resthelper._assert_result_data, - fake_result, - self.fake_message) - - def test__create_nfs_share(self): - mock_call = self.mock_object(self.resthelper, - 'call') - self.mock_object(self.resthelper, - '_assert_result_code') - self.resthelper._create_nfs_share('fake_path') - url = 'rest/nfsShare' - data = { - 'path': 'fake_path', - 'authority': 'ro', - 'accessClient': '192.0.2.0', - } - mock_call.assert_called_once_with(url, data, self.post) - - def test__get_nfs_share(self): - fake_result = { - 'code': 0, - 'message': 'success', - 'data': { - "path": "fake_path", - "clients": ["client"], - "protocol": "fake_protocol" - } - } - mock_call = self.mock_object(self.resthelper, - 'call', - mock.Mock(return_value=fake_result)) - self.mock_object(self.resthelper, - '_assert_result_code') - result = self.resthelper._get_nfs_share('fake_path') - expect = { - "path": "fake_path", - "clients": ["client"], - "protocol": "fake_protocol" - } - self.assertEqual(expect, result) - url = 'rest/nfsShare?path=fake_path' - mock_call.assert_called_once_with(url, None, self.get) - - def test__delete_nfs_share(self): - mock_call = self.mock_object(self.resthelper, - 'call') - self.mock_object(self.resthelper, - '_assert_result_code') - self.resthelper._delete_nfs_share('fake_path') - url = 'rest/nfsShare?path=fake_path' - mock_call.assert_called_once_with(url, None, self.delete) - - def test__create_cifs_share(self): - mock_call = self.mock_object(self.resthelper, - 'call') - self.mock_object(self.resthelper, - '_assert_result_code') - self.resthelper._create_cifs_share('fake_name', - 'fake_path', - ['fake_user'], - ['0']) - url = 'rest/cifsShare' - data = { - 'path': 'fake_path', - 'cifsName': 'fake_name', - 'cifsDescription': '', - 'RoList': [], - 'RoListType': [], - 'RwList': ['fake_user'], - 'RwListType': ['0'], - 'allowList': [], - 'denyList': [], - } - mock_call.assert_called_once_with(url, data, self.post) - - def test__get_cifs_share(self): - fake_result = { - 'code': 0, - 'message': 'success', - 'data': { - "path": "fake_path", - "cifsname": "fake_cifsname", - "protocol": "fake_protocol", - "roList": ["fake_ro"], - "rwList": ["fake_rw"], - "allowList": ["fake_allow"], - "denyList": ["fake_deny"] - } - } - mock_call = self.mock_object(self.resthelper, - 'call', - mock.Mock(return_value=fake_result)) - self.mock_object(self.resthelper, - '_assert_result_code') - result = self.resthelper._get_cifs_share('fake_path') - expect = { - "path": "fake_path", - "cifsname": "fake_cifsname", - "protocol": "fake_protocol", - "roList": ["fake_ro"], - "rwList": ["fake_rw"], - "allowList": ["fake_allow"], - "denyList": ["fake_deny"] - } - self.assertEqual(expect, result) - url = 'rest/cifsShare?path=fake_path' - mock_call.assert_called_once_with(url, None, self.get) - - def test__delete_cifs_share(self): - mock_call = self.mock_object(self.resthelper, - 'call') - self.mock_object(self.resthelper, - '_assert_result_code') - self.resthelper._delete_cifs_share('fake_name', 'fake_path') - url = 'rest/cifsShare?path=fake_path&cifsName=fake_name' - mock_call.assert_called_once_with(url, None, self.delete) - - def test__update_share_size(self): - mock_call = self.mock_object(self.resthelper, - 'call') - self.mock_object(self.resthelper, - '_assert_result_code') - self.resthelper._update_share_size('fake_filesystem', '2GB') - url = 'rest/filesystem/fake_filesystem' - data = { - 'capacity': '2GB', - } - mock_call.assert_called_once_with(url, data, self.put) - - def test___create_filesystem(self): - mock_call = self.mock_object(self.resthelper, - 'call') - self.mock_object(self.resthelper, - '_assert_result_code') - self.resthelper._create_filesystem('fake_filesystem', - 'fake_pool', - '1GB') - url = 'rest/filesystem' - data = { - 'fsName': 'fake_filesystem', - 'poolName': 'fake_pool', - 'createType': '0', - 'fileSystemQuota': '1GB', - 'fileSystemReserve': '1GB', - 'wormStatus': 0, - 'defaultTimeStatus': 0, - 'defaultTimeNum': 0, - 'defaultTimeUnit': 'year', - 'isAutoLock': 0, - 'isAutoDelete': 0, - 'lockTime': 0 - } - mock_call.assert_called_once_with(url, data, self.post) - - def test__delete_filesystem(self): - mock_call = self.mock_object(self.resthelper, - 'call') - self.mock_object(self.resthelper, - '_assert_result_code') - self.resthelper._delete_filesystem('fake_filesystem') - url = 'rest/filesystem/fake_filesystem' - mock_call.assert_called_once_with(url, None, self.delete) - - def test__get_filesystem(self): - fake_result = { - 'code': 0, - 'message': 'success', - 'data': { - 'name': 'fake_filesystem', - 'poolName': 'fake_pool', - } - } - mock_call = self.mock_object(self.resthelper, - 'call', - mock.Mock(return_value=fake_result)) - self.mock_object(self.resthelper, - '_assert_result_code') - result = self.resthelper._get_filesystem('fake_filesystem') - expect = { - 'name': 'fake_filesystem', - 'poolName': 'fake_pool', - } - self.assertEqual(expect, result) - url = 'rest/filesystem/fake_filesystem' - mock_call.assert_called_once_with(url, None, self.get) - - def test__create_filesystem_dir(self): - mock_call = self.mock_object(self.resthelper, - 'call') - self.mock_object(self.resthelper, - '_assert_result_code') - self.resthelper._create_filesystem_dir('/fake_path/fake_dir') - url = 'rest/fileDir' - data = { - 'path': '/fake_path', - 'dirName': 'fake_dir', - } - mock_call.assert_called_once_with(url, data, self.post) - - def test__delete_filesystem_dir(self): - mock_call = self.mock_object(self.resthelper, - 'call') - self.mock_object(self.resthelper, - '_assert_result_code') - self.resthelper._delete_filesystem_dir('/fake_path/fake_dir') - url = 'rest/fileDir?path=/fake_path&dirName=fake_dir' - mock_call.assert_called_once_with(url, None, self.delete) - - @ddt.data('nfs', 'cifs') - def test__allow_access_rest(self, share_proto): - share_proto = share_proto.upper() - mock_anar = self.mock_object(self.resthelper, - '_allow_nfs_access_rest') - mock_acar = self.mock_object(self.resthelper, - '_allow_cifs_access_rest') - self.resthelper._allow_access_rest('fake_path', 'fake_access', - 'rw', share_proto) - if share_proto == 'NFS': - mock_anar.assert_called_once_with('fake_path', - 'fake_access', - 'rw') - elif share_proto == 'CIFS': - mock_acar.assert_called_once_with('fake_path', - 'fake_access', - 'rw') - - def test__allow_access_rest_proto_error(self): - self.assertRaises(exception.InvalidInput, - self.resthelper._allow_access_rest, - 'fake_path', - 'fake_access', - 'rw', - 'error_proto') - - def test__allow_nfs_access_rest(self): - mock_call = self.mock_object( - self.resthelper, - 'call', - mock.Mock(return_value=self.result_success)) - self.mock_object(self.resthelper, - '_assert_result_code') - self.resthelper._allow_nfs_access_rest('fake_path', '172.0.0.1', 'rw') - url = 'rest/nfsShareClient' - data = { - 'path': 'fake_path', - 'client': '172.0.0.1', - 'authority': 'rw', - } - mock_call.assert_called_once_with(url, data, self.post) - - @ddt.data( - {'access_to': 'fake_user', - 'group': False}, - {'access_to': 'fake_group', - 'group': True}, - {'access_to': '/fake_user', - 'group': False}, - {'access_to': '/fake_group', - 'group': True} - ) - @ddt.unpack - def test__allow_cifs_access_rest(self, access_to, group): - ug_type_list = { - 'localUser': '0', - 'localGroup': '1', - 'adUser': '2', - 'adGroup': '3', - } - if not group: - mock_call = self.mock_object( - self.resthelper, - 'call', - mock.Mock(return_value=self.result_success)) - else: - mock_call = self.mock_object( - self.resthelper, - 'call', - mock.Mock(side_effect=[self.result_failed_not_exist, - self.result_success])) - self.mock_object(self.resthelper, - '_assert_result_code') - self.resthelper._allow_cifs_access_rest('fake_path', - access_to, - 'rw') - url = 'rest/cifsShareClient' - actual_type = ug_type_list["localUser"] - if '/' not in access_to: - if not group: - actual_type = ug_type_list["localUser"] - access_to = access_to - else: - if not group: - actual_type = ug_type_list["adUser"] - access_to = access_to[access_to.index('/') + 1:] - data = { - 'path': 'fake_path', - 'right': 'rw', - 'ugName': access_to, - 'ugType': actual_type, - } - if not group: - mock_call.assert_called_once_with(url, data, self.post) - else: - mock_call.assert_called() - - def test__allow_cifs_access_rest_fail(self): - mock_call = self.mock_object( - self.resthelper, - 'call', - mock.Mock(side_effect=[self.result_failed_not_exist, - self.result_failed_not_exist])) - self.assertRaises(exception.InvalidShare, - self.resthelper._allow_cifs_access_rest, - 'fake_path', - 'fake_user', - 'rw') - mock_call.assert_called() - - def test__get_access_from_nfs_share(self): - fake_result = { - 'code': 0, - 'message': 'success', - 'data': { - "path": "fake_path", - "clientName": "fake_client", - "accessRight": "rw", - } - } - mock_call = self.mock_object(self.resthelper, - 'call', - mock.Mock(return_value=fake_result)) - self.mock_object(self.resthelper, - '_assert_result_code') - result = self.resthelper._get_access_from_nfs_share('fake_path', - 'fake_client') - expect = { - "path": "fake_path", - "clientName": "fake_client", - "accessRight": "rw", - } - self.assertEqual(expect, result) - url = 'rest/nfsShareClient?path=fake_path&client=fake_client' - mock_call.assert_called_once_with(url, None, self.get) - - @ddt.data({'access_to': 'fake_user', - 'ug_type': '0', - 'code': 0, - 'group': False}, - {'access_to': 'fake_user', - 'ug_type': None, - 'code': 0, - 'group': False}, - {'access_to': 'fake_group', - 'ug_type': None, - 'code': 0, - 'group': True}, - {'access_to': 'fake_user', - 'ug_type': None, - 'code': 4, - 'group': False}, - {'access_to': '/fake_user', - 'ug_type': None, - 'code': 0, - 'group': False}, - {'access_to': '/fake_group', - 'ug_type': None, - 'code': 0, - 'group': True}, - {'access_to': '/fake_user', - 'ug_type': None, - 'code': 4, - 'group': False}) - @ddt.unpack - def test__get_access_from_cifs_share(self, - access_to, ug_type, code, group): - fake_result_failed = { - 'code': code, - 'message': 'failed', - 'data': {} - } - fake_result = { - 'code': code, - 'message': 'success', - 'data': { - 'path': 'fake_path', - 'ugName': 'fake_user', - 'ugType': '0', - 'accessRight': 'rw' - } - } - fake_result_group = { - 'code': code, - 'message': 'success', - 'data': { - 'path': 'fake_path', - 'ugName': 'fake_group', - 'ugType': '1', - 'accessRight': 'rw' - } - } - if code == 4: - fake_result = fake_result_failed - ug_type_list = { - 'localUser': '0', - 'localGroup': '1', - 'adUser': '2', - 'adGroup': '3', - } - expect = { - 'path': 'fake_path', - 'ugName': 'fake_user', - 'ugType': '0', - 'accessRight': 'rw' - } - expect_group = { - 'path': 'fake_path', - 'ugName': 'fake_group', - 'ugType': '1', - 'accessRight': 'rw' - } - if '/' in access_to: - expect['ugType'] = '2' - expect_group['ugType'] = '3' - fake_result['data']['ugType'] = '2' - fake_result_group['data']['ugType'] = '3' - if ug_type is not None: - mock_call = self.mock_object(self.resthelper, - 'call', - mock.Mock(return_value=fake_result)) - else: - if not group: - mock_call = self.mock_object( - self.resthelper, - 'call', - mock.Mock(return_value=fake_result)) - else: - mock_call = self.mock_object( - self.resthelper, - 'call', - mock.Mock(side_effect=[fake_result_failed, - fake_result_group])) - - self.mock_object(self.resthelper, - '_assert_result_code') - result = self.resthelper._get_access_from_cifs_share('fake_path', - access_to, - ug_type) - if ug_type: - self.assertEqual(expect, result) - url = f'rest/cifsShareClient?path=fake_path&' \ - f'ugName={access_to}&ugType={ug_type}' - mock_call.assert_called_once_with(url, None, self.get) - else: - if '/' not in access_to: - if not group: - actual_type = ug_type_list["localUser"] - actual_access = access_to - else: - if not group: - actual_type = ug_type_list["adUser"] - actual_access = access_to[access_to.index('/') + 1:] - if code == 4: - self.assertIsNone(result) - else: - if not group: - self.assertEqual(expect, result) - url = f'rest/cifsShareClient?path=fake_path&' \ - f'ugName={actual_access}&' \ - f'ugType={actual_type}' - mock_call.assert_called_once_with(url, None, self.get) - else: - self.assertEqual(expect_group, result) - mock_call.assert_called() - - def test__get_all_nfs_access_rest(self): - fake_result = { - 'code': 0, - 'message': 'success', - 'data': [ - { - 'path': 'fake_path', - 'clientName': '172.0.0.1', - 'accessRight': 'rw' - }, - { - 'path': 'default_path', - 'clientName': '172.0.0.2', - 'accessRight': 'rw' - }] - } - mock_call = self.mock_object(self.resthelper, - 'call', - mock.Mock(return_value=fake_result)) - self.mock_object(self.resthelper, - '_assert_result_code') - result = self.resthelper._get_all_nfs_access_rest( - '/manila_fakeid/manila_fakeid') - expect = [ - { - 'share_path': 'fake_path', - 'access_to': '172.0.0.1', - 'access_level': 'rw' - }, - { - 'share_path': 'default_path', - 'access_to': '172.0.0.2', - 'access_level': 'rw' - }] - self.assertEqual(expect, result) - url = 'rest/allNfsShareClient?path=/manila_fakeid/manila_fakeid' - mock_call.assert_called_once_with(url, None, self.get) - - def test__get_all_cifs_access_rest(self): - fake_result = { - 'code': 0, - 'message': 'success', - 'data': [ - { - 'path': 'fake_path', - 'ugName': 'user_name', - 'ugType': '0', - 'accessRight': 'rw' - }, - { - 'path': 'default_path', - 'ugName': 'manilanobody', - 'ugType': '0', - 'accessRight': 'rw' - }] - } - mock_call = self.mock_object(self.resthelper, - 'call', - mock.Mock(return_value=fake_result)) - self.mock_object(self.resthelper, - '_assert_result_code') - result = self.resthelper._get_all_cifs_access_rest( - '/manila_fakeid/manila_fakeid') - expect = [ - { - 'share_path': 'fake_path', - 'access_to': 'user_name', - 'ugType': '0', - 'access_level': 'rw' - }, - { - 'share_path': 'default_path', - 'access_to': 'manilanobody', - 'ugType': '0', - 'access_level': 'rw' - }] - self.assertEqual(expect, result) - url = 'rest/allCifsShareClient?path=/manila_fakeid/manila_fakeid' - mock_call.assert_called_once_with(url, None, self.get) - - def test__change_nfs_access_rest(self): - mock_call = self.mock_object(self.resthelper, - 'call') - self.mock_object(self.resthelper, - '_assert_result_code') - self.resthelper._change_nfs_access_rest( - '/manila_fakeid/manila_fakeid', '172.0.0.1', 'rw') - url = 'rest/nfsShareClient' - data = { - 'path': '/manila_fakeid/manila_fakeid', - 'oldNfsClientName': '172.0.0.1', - 'clientName': '', - 'accessRight': 'rw', - 'allSquash': '', - 'rootSquash': '', - 'secure': '', - 'anonuid': '', - 'anongid': '', - } - mock_call.assert_called_once_with(url, data, self.put) - - def test__change_cifs_access_rest(self): - mock_call = self.mock_object(self.resthelper, - 'call') - self.mock_object(self.resthelper, - '_assert_result_code') - self.resthelper._change_cifs_access_rest( - '/manila_fakeid/manila_fakeid', '/fake_user', 'rw', '0') - url = 'rest/cifsShareClient' - data = { - 'path': '/manila_fakeid/manila_fakeid', - 'right': 'rw', - 'ugName': 'fake_user', - 'ugType': '0', - } - mock_call.assert_called_once_with(url, data, self.put) - - def test__delete_nfs_access_rest(self): - mock_call = self.mock_object(self.resthelper, - 'call') - self.mock_object(self.resthelper, - '_assert_result_code') - self.resthelper._delete_nfs_access_rest( - '/manila_fakeid/manila_fakeid', '*') - url = 'rest/nfsShareClient?path=/manila_fakeid/manila_fakeid&client=*' - mock_call.assert_called_once_with(url, None, self.delete) - - def test__delete_cifs_access_rest(self): - mock_call = self.mock_object(self.resthelper, - 'call') - self.mock_object(self.resthelper, - '_assert_result_code') - self.resthelper._delete_cifs_access_rest( - '/manila_fakeid/manila_fakeid', 'fake_user', '0') - url = 'rest/cifsShareClient?path=/manila_fakeid/manila_fakeid' \ - '&ugName=fake_user&ugType=0' - mock_call.assert_called_once_with(url, None, self.delete) - - def test__get_nfs_service_status(self): - fake_result = { - 'code': 0, - 'message': 'success', - 'data': { - 'serviceStatus': constants.NFS_ENABLED, - 'nfs3Status': constants.NFS_SUPPORTED, - 'nfs4Status': constants.NFS_SUPPORTED - } - } - mock_call = self.mock_object(self.resthelper, - 'call', - mock.Mock(return_value=fake_result)) - self.mock_object(self.resthelper, - '_assert_result_code') - result = self.resthelper._get_nfs_service_status() - expect = { - 'serviceStatus': constants.NFS_ENABLED, - 'nfs3Status': constants.NFS_SUPPORTED, - 'nfs4Status': constants.NFS_SUPPORTED - } - self.assertEqual(expect, result) - url = 'rest/nfsService' - mock_call.assert_called_once_with(url, None, self.get) - - def test__start_nfs_service(self): - mock_call = self.mock_object(self.resthelper, - 'call') - self.mock_object(self.resthelper, - '_assert_result_code') - self.resthelper._start_nfs_service() - url = 'rest/nfsService' - data = { - "openStatus": "1", - } - mock_call.assert_called_once_with(url, data, self.put) - - def test__config_nfs_service(self): - mock_call = self.mock_object(self.resthelper, - 'call') - self.mock_object(self.resthelper, - '_assert_result_code') - self.resthelper._config_nfs_service() - url = 'rest/nfsConfig' - data = { - 'configNfs3': "yes", - 'configNfs4': "yes", - } - mock_call.assert_called_once_with(url, data, self.put) - - def test__get_cifs_service_status(self): - mock_call = self.mock_object( - self.resthelper, - 'call', - mock.Mock(return_value=self.result_success_return_1)) - self.mock_object(self.resthelper, - '_assert_result_code') - result = self.resthelper._get_cifs_service_status() - self.assertEqual('1', result) - url = 'rest/cifsService' - mock_call.assert_called_once_with(url, None, self.get) - - def test__start_cifs_service(self): - mock_call = self.mock_object(self.resthelper, - 'call') - self.mock_object(self.resthelper, - '_assert_result_code') - self.resthelper._start_cifs_service() - url = 'rest/cifsService' - data = { - 'openStatus': '1', - } - mock_call.assert_called_once_with(url, data, self.put) - - def test__config_cifs_service(self): - mock_call = self.mock_object(self.resthelper, - 'call') - self.mock_object(self.resthelper, - '_assert_result_code') - self.resthelper._config_cifs_service() - url = 'rest/cifsConfig' - data = { - 'workName': 'manila', - 'description': '', - 'access_way': 'user', - 'isCache': 'no', - 'adsName': '', - 'adsIP': '', - 'adsUSER': '', - 'adsPASSWD': '', - 'allowList': [], - 'denyList': [], - } - mock_call.assert_called_once_with(url, data, self.put) - - def test__get_all_pool(self): - mock_call = self.mock_object( - self.resthelper, - 'call', - mock.Mock(return_value=self.result_success_storage_pools)) - self.mock_object(self.resthelper, - '_assert_result_code') - result = self.resthelper._get_all_pool() - self.assertEqual(self.result_success_storage_pools, result) - url = 'rest/storagepool' - mock_call.assert_called_once_with(url, None, self.get) - - def test__query_user(self): - mock_call = self.mock_object( - self.resthelper, - 'call', - mock.Mock(return_value=self.result_success_return_0)) - self.mock_object(self.resthelper, - '_assert_result_code') - result = self.resthelper._query_user('fake_user') - self.assertEqual('0', result) - url = 'rest/user/fake_user' - mock_call.assert_called_once_with(url, None, self.get) - - def test__add_localuser(self): - mock_call = self.mock_object(self.resthelper, - 'call') - self.mock_object(self.resthelper, - '_assert_result_code') - self.resthelper._add_localuser('fake_user', - 'fake_passwd', 'fake_group') - url = 'rest/localUser' - data = { - 'userName': 'fake_user', - 'mgGroup': 'fake_group', - 'userPasswd': 'fake_passwd', - 'unusedGroup': []} - mock_call.assert_called_once_with(url, data, self.post) - - def test__query_group(self): - mock_call = self.mock_object( - self.resthelper, - 'call', - mock.Mock(return_value=self.result_success_return_0)) - self.mock_object(self.resthelper, - '_assert_result_code') - result = self.resthelper._query_group('fake_group') - self.assertEqual('0', result) - url = 'rest/group/fake_group' - mock_call.assert_called_once_with(url, None, self.get) - - def test__add_localgroup(self): - mock_call = self.mock_object(self.resthelper, - 'call') - self.mock_object(self.resthelper, - '_assert_result_code') - self.resthelper._add_localgroup('fake_group') - url = 'rest/localGroup' - data = {'groupName': 'fake_group'} - mock_call.assert_called_once_with(url, data, self.post) +# Copyright (c) 2022 MacroSAN Technologies Co., Ltd. +# 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. + +""" +Share driver test for Macrosan Storage Array. +""" +import ddt +import requests + +from oslo_config import cfg +from unittest import mock + +from manila import context +from manila import exception +from manila.share import configuration +from manila.share import driver +from manila.share.drivers.macrosan import macrosan_constants as constants +from manila.share.drivers.macrosan import macrosan_helper +from manila.share.drivers.macrosan import macrosan_nas +from manila.share.drivers.macrosan import rest_helper +from manila import test +from manila.tests import fake_share + +CONF = cfg.CONF + + +class FakeResponse(object): + def __init__(self, status, result): + self.status_code = status + self.text = 'return message' + self.response = result + + def json(self): + return self.response + + def close(self): + pass + + +@ddt.ddt +class MacrosanShareDriverTestCase(test.TestCase): + + def setUp(self): + self.mock_object(macrosan_nas.CONF, '_check_required_opts') + super(MacrosanShareDriverTestCase, self).setUp() + + def _safe_get(opt): + return getattr(self.configuration, opt) + + self._context = context.get_admin_context() + self.configuration = mock.Mock(spec=configuration.Configuration) + self.configuration.safe_get = mock.Mock(side_effect=_safe_get) + self.configuration.driver_handles_share_servers = False + self.configuration.share_backend_name = 'fake_share_backend_name' + self.configuration.macrosan_nas_http_protocol = 'https' + self.configuration.macrosan_nas_ip = 'fake_ip' + self.configuration.macrosan_nas_port = 'fake_port' + self.configuration.macrosan_nas_username = 'fake_username' + self.configuration.macrosan_nas_password = 'fake_password' + self.configuration.macrosan_nas_prefix = 'nas' + self.configuration.macrosan_share_pools = ['fake_pool'] + self.configuration.macrosan_timeout = 60 + self.configuration.macrosan_ssl_cert_verify = False + + self.configuration.network_config_group = 'fake_network_config_group' + self.configuration.admin_network_config_group = ( + 'fake_admin_network_config_group') + self.configuration.config_group = 'fake_config_group' + self.configuration.reserved_share_percentage = 0 + self.configuration.reserved_share_from_snapshot_percentage = 0 + self.configuration.reserved_share_extend_percentage = 0 + self.configuration.filter_function = None + self.configuration.goodness_function = None + self.driver = macrosan_nas.MacrosanNasDriver( + configuration=self.configuration) + self.result_success_storage_pools = { + 'code': 0, + 'message': 'success', + 'data': [{ + 'name': 'fake_pool', + 'size': '1000.0G', + 'allocated': '100G', + 'free': '900G', + 'health': 'ONLINE', + 'rwStatus': 'off' + }] + } + + def test_do_setup(self): + mock_login = self.mock_object(rest_helper.RestHelper, 'login') + self.driver.do_setup(self._context) + mock_login.assert_called_once() + + def test_do_setup_login_fail(self): + mock_login = self.mock_object( + rest_helper.RestHelper, 'login', + mock.Mock( + side_effect=exception.ShareBackendException( + msg='fake_exception'))) + self.assertRaises(exception.ShareBackendException, + self.driver.do_setup, + self._context) + mock_login.assert_called_once() + + @ddt.data({'nfs_status': constants.NFS_NON_CONFIG, + 'cifs_status': constants.CIFS_NON_CONFIG}, + {'nfs_status': constants.NFS_DISABLED, + 'cifs_status': constants.CIFS_DISABLED}, + {'nfs_status': constants.NFS_ENABLED, + 'cifs_status': constants.CIFS_ENABLED}, + {'nfs_status': constants.NFS_ENABLED, + 'cifs_status': constants.CIFS_SHARE_MODE}) + @ddt.unpack + def test_check_for_setup_error_non_config(self, nfs_status, cifs_status): + mock_gnss = self.mock_object( + rest_helper.RestHelper, '_get_nfs_service_status', + mock.Mock(return_value={ + "serviceStatus": nfs_status, + "nfs3Status": constants.NFS_NON_SUPPORTED, + "nfs4Status": constants.NFS_NON_SUPPORTED + })) + + mock_cns = self.mock_object(rest_helper.RestHelper, + '_config_nfs_service') + mock_sns = self.mock_object(rest_helper.RestHelper, + '_start_nfs_service') + if cifs_status == constants.CIFS_DISABLED: + mock_gcss = self.mock_object( + rest_helper.RestHelper, '_get_cifs_service_status', + mock.Mock(side_effect=[cifs_status, + constants.CIFS_SHARE_MODE])) + else: + mock_gcss = self.mock_object( + rest_helper.RestHelper, '_get_cifs_service_status', + mock.Mock(return_value=cifs_status)) + mock_ccs = self.mock_object(rest_helper.RestHelper, + '_config_cifs_service') + mock_scs = self.mock_object(rest_helper.RestHelper, + '_start_cifs_service') + self.driver.check_for_setup_error() + if (nfs_status == constants.NFS_NON_CONFIG or + nfs_status == constants.NFS_DISABLED): + mock_cns.assert_called_once() + mock_sns.assert_called_once() + else: + mock_cns.assert_called_once() + mock_gnss.assert_called_once() + if cifs_status == constants.CIFS_NON_CONFIG: + mock_gcss.assert_called_once() + mock_ccs.assert_called_once() + mock_scs.assert_called_once() + elif cifs_status == constants.CIFS_DISABLED: + mock_gcss.assert_called() + mock_ccs.assert_called_once() + mock_scs.assert_called_once() + elif cifs_status == constants.CIFS_SHARE_MODE: + mock_gcss.assert_called_once() + mock_ccs.assert_called_once() + else: + mock_gcss.assert_called_once() + + def test_check_for_setup_error_nfs_service_error(self): + mock_gnss = self.mock_object( + rest_helper.RestHelper, '_get_nfs_service_status', + mock.Mock(return_value={ + "serviceStatus": constants.NFS_EXCEPTION, + "nfs3Status": constants.NFS_NON_SUPPORTED, + "nfs4Status": constants.NFS_NON_SUPPORTED + })) + self.assertRaises(exception.MacrosanBackendExeption, + self.driver.check_for_setup_error) + mock_gnss.assert_called_once() + + def test_check_for_setup_error_cifs_service_error(self): + mock_gnss = self.mock_object( + rest_helper.RestHelper, '_get_nfs_service_status', + mock.Mock(return_value={ + "serviceStatus": constants.NFS_ENABLED, + "nfs3Status": constants.NFS_SUPPORTED, + "nfs4Status": constants.NFS_SUPPORTED + })) + mock_gcss = self.mock_object( + rest_helper.RestHelper, '_get_cifs_service_status', + mock.Mock(return_value=constants.CIFS_EXCEPTION)) + self.assertRaises(exception.MacrosanBackendExeption, + self.driver.check_for_setup_error) + mock_gnss.assert_called_once() + mock_gcss.assert_called_once() + + @ddt.data('nfs', 'cifs') + def test_create_share(self, share_proto): + share = fake_share.fake_share( + share_proto=share_proto, host="fake_host@fake_backend#fake_pool") + mock_cf = self.mock_object(rest_helper.RestHelper, + '_create_filesystem') + mock_cfd = self.mock_object(rest_helper.RestHelper, + '_create_filesystem_dir') + mock_cns = self.mock_object(rest_helper.RestHelper, + '_create_nfs_share') + self.mock_object(macrosan_helper.MacrosanHelper, + '_ensure_user', + mock.Mock(return_value=True)) + mock_ccs = self.mock_object(rest_helper.RestHelper, + '_create_cifs_share') + self.driver.helper.configuration.macrosan_nas_ip = "172.0.0.1" + + location = self.driver.create_share(self._context, share) + if share_proto == 'nfs': + expect_location = r'172.0.0.1:/manila_fakeid/manila_fakeid' + print('test location:', location) + self.assertEqual(location, expect_location) + else: + expect_location = r'\\172.0.0.1\manila_fakeid' + self.assertEqual(location, expect_location) + mock_cf.assert_called_once_with(fs_name='manila_fakeid', + pool_name='fake_pool', + filesystem_quota='1GB') + mock_cf.assert_called() + share_path = self.driver.helper._generate_share_path('manila_fakeid') + mock_cfd.assert_called_once_with(share_path) + if share_proto == 'nfs': + mock_cns.assert_called_once_with(share_path=share_path) + else: + mock_ccs.assert_called_once() + + def test_create_share_user_error(self): + share = fake_share.fake_share( + share_proto='cifs', host="fake_host@fake_backend#fake_pool") + mock_cf = self.mock_object(rest_helper.RestHelper, + '_create_filesystem') + mock_cfd = self.mock_object(rest_helper.RestHelper, + '_create_filesystem_dir') + self.mock_object(macrosan_helper.MacrosanHelper, + '_ensure_user', + mock.Mock(return_value=False)) + mock_df = self.mock_object(rest_helper.RestHelper, + '_delete_filesystem') + self.assertRaises(exception.MacrosanBackendExeption, + self.driver.create_share, + self._context, + share) + mock_cf.assert_called_once() + share_path = self.driver.helper._generate_share_path('manila_fakeid') + mock_cfd.assert_called_once_with(share_path) + mock_df.assert_called_once_with('manila_fakeid') + + @ddt.data('nfs', 'cifs') + def test_delete_share(self, share_proto): + share = fake_share.fake_share( + share_proto=share_proto, host="fake_host@fake_backend#fake_pool") + expect_share_path = self.driver.helper._generate_share_path( + 'manila_fakeid') + + mock_gns = self.mock_object(rest_helper.RestHelper, '_get_nfs_share', + mock.Mock(return_value={ + "path": "fake_path", + "clients": ["client"], + "protocol": "fake_protocol" + })) + mock_dns = self.mock_object( + rest_helper.RestHelper, '_delete_nfs_share') + mock_gcs = self.mock_object(rest_helper.RestHelper, '_get_cifs_share', + mock.Mock(return_value={ + "path": "fake_path", + "cifsname": "fake_cifsname", + "protocol": "fake_protocol", + "roList": ["fake_ro"], + "rwList": ["fake_rw"], + "allowList": ["fake_allow"], + "denyList": ["fake_deny"] + })) + mock_dcs = self.mock_object(rest_helper.RestHelper, + '_delete_cifs_share') + mock_df = self.mock_object(rest_helper.RestHelper, + '_delete_filesystem') + self.driver.delete_share(self._context, share) + + if share_proto == "nfs": + mock_gns.assert_called_once_with(expect_share_path) + mock_dns.assert_called_once_with(expect_share_path) + else: + mock_gcs.assert_called_once_with(expect_share_path) + mock_dcs.assert_called_once_with('manila_fakeid', + expect_share_path) + mock_df.assert_called_once_with('manila_fakeid') + + @ddt.data('nfs', 'cifs') + def test_delete_share_not_exist(self, share_proto): + share = fake_share.fake_share(share_proto=share_proto, + host="fake_host@fake_backend#fake_pool") + expect_share_path = self.driver.helper._generate_share_path( + 'manila_fakeid') + mock_gns = self.mock_object(rest_helper.RestHelper, '_get_nfs_share', + mock.Mock(return_value=None)) + mock_gf = self.mock_object(rest_helper.RestHelper, '_get_filesystem', + mock.Mock(return_value={ + "name": "fake_name", + "poolName": "fake_pool", + "quotaStatus": "1GB" + })) + mock_gcs = self.mock_object(rest_helper.RestHelper, '_get_cifs_share', + mock.Mock(return_value=None)) + mock_df = self.mock_object(rest_helper.RestHelper, + '_delete_filesystem') + self.driver.delete_share(self._context, share) + + if share_proto == 'nfs': + mock_gns.assert_called_once_with(expect_share_path) + else: + mock_gcs.assert_called_once_with(expect_share_path) + + mock_gf.assert_called_once_with('manila_fakeid') + mock_df.assert_called_once_with('manila_fakeid') + + @ddt.data('nfs', 'cifs') + def test_extend_share(self, share_proto): + share = fake_share.fake_share(share_proto=share_proto, + host="fake_host@fake_backend#fake_pool") + expect_share_path = self.driver.helper._generate_share_path( + 'manila_fakeid') + mock_gns = self.mock_object(rest_helper.RestHelper, '_get_nfs_share', + mock.Mock(return_value={ + "path": "fake_path", + "clients": ["client"], + "protocol": "fake_protocol" + })) + mock_gcs = self.mock_object(rest_helper.RestHelper, '_get_cifs_share', + mock.Mock(return_value={ + "path": "fake_path", + "cifsname": "fake_cifsname", + "protocol": "fake_protocol", + "roList": ["fake_ro"], + "rwList": ["fake_rw"], + "allowList": ["fake_allow"], + "denyList": ["fake_deny"] + })) + mock_uss = self.mock_object(rest_helper.RestHelper, + '_update_share_size') + self.driver.extend_share(share, 2) + + if share_proto == 'nfs': + mock_gns.assert_called_once_with(expect_share_path) + else: + mock_gcs.assert_called_once_with(expect_share_path) + + mock_uss.assert_called_once_with('manila_fakeid', '2GB') + + def test_extend_share_not_exist(self): + share = fake_share.fake_share(share_proto='nfs', + size=1, + host="fake_host@fake_backend#fake_pool") + expect_share_path = self.driver.helper._generate_share_path( + 'manila_fakeid') + mock_gns = self.mock_object(rest_helper.RestHelper, + '_get_nfs_share', + mock.Mock(return_value=None)) + self.assertRaises(exception.ShareResourceNotFound, + self.driver.extend_share, + share, + 2) + + mock_gns.assert_called_once_with(expect_share_path) + + @ddt.data('nfs', 'cifs') + def test_shrink_share(self, share_proto): + share = fake_share.fake_share(share_proto=share_proto, + size=5, + host="fake_host@fake_backend#fake_pool") + expect_share_path = self.driver.helper._generate_share_path( + 'manila_fakeid') + mock_gns = self.mock_object(rest_helper.RestHelper, + '_get_nfs_share', + mock.Mock(return_value={ + "path": "fake_path", + "clients": ["client"], + "protocol": "fake_protocol" + })) + mock_gcs = self.mock_object(rest_helper.RestHelper, '_get_cifs_share', + mock.Mock(return_value={ + "path": "fake_path", + "cifsname": "fake_cifsname", + "protocol": "fake_protocol", + "roList": ["fake_ro"], + "rwList": ["fake_rw"], + "allowList": ["fake_allow"], + "denyList": ["fake_deny"] + })) + mock_gf = self.mock_object(rest_helper.RestHelper, '_get_filesystem', + mock.Mock(return_value={ + "name": "fake_name", + "poolName": "fake_pool", + "quotaStatus": "5GB", + "usedCapacity": '1GB' + })) + mock_uss = self.mock_object(rest_helper.RestHelper, + '_update_share_size') + self.driver.shrink_share(share, 3) + if share_proto == 'nfs': + mock_gns.assert_called_once_with(expect_share_path) + else: + mock_gcs.assert_called_once_with(expect_share_path) + + mock_gf.assert_called_once_with('manila_fakeid') + mock_uss.assert_called_once_with('manila_fakeid', '3GB') + + @ddt.data('nfs', 'cifs') + def test_shrink_share_not_exist(self, share_proto): + share = fake_share.fake_share(share_proto=share_proto, + size=3, + host="fake_host@fake_backend#fake_pool") + expect_share_path = self.driver.helper._generate_share_path( + 'manila_fakeid') + mock_gns = self.mock_object(rest_helper.RestHelper, '_get_nfs_share', + mock.Mock(return_value=None)) + mock_gcs = self.mock_object(rest_helper.RestHelper, '_get_cifs_share', + mock.Mock(return_value=None)) + + self.assertRaises(exception.ShareResourceNotFound, + self.driver.shrink_share, + share, + 1) + if share_proto == 'nfs': + mock_gns.assert_called_once_with(expect_share_path) + elif share_proto == 'cifs': + mock_gcs.assert_called_once_with(expect_share_path) + + def test_shrink_share_size_fail(self): + share = fake_share.fake_share(share_proto='nfs', + size=3, + host="fake_host@fake_backend#fake_pool") + expect_share_path = self.driver.helper._generate_share_path( + 'manila_fakeid') + mock_gns = self.mock_object(rest_helper.RestHelper, + '_get_nfs_share', + mock.Mock(return_value={ + "path": "fake_path", + "clients": ["client"], + "protocol": "fake_protocol" + })) + mock_gf = self.mock_object(rest_helper.RestHelper, '_get_filesystem', + mock.Mock(return_value={ + "name": "fake_name", + "poolName": "fake_pool", + "quotaStatus": "3GB", + "usedCapacity": '2GB' + })) + self.assertRaises(exception.ShareShrinkingPossibleDataLoss, + self.driver.shrink_share, + share, + 1) + mock_gf.assert_called_once_with('manila_fakeid') + mock_gns.assert_called_once_with(expect_share_path) + + @ddt.data('nfs', 'cifs') + def test_ensure_share(self, share_proto): + share = fake_share.fake_share(share_proto=share_proto, + host="fake_host@fake_backend#fake_pool") + mock_gns = self.mock_object(rest_helper.RestHelper, '_get_nfs_share', + mock.Mock(return_value={ + "path": "fake_path", + "clients": ["client"], + "protocol": "fake_protocol" + })) + mock_gcs = self.mock_object(rest_helper.RestHelper, '_get_cifs_share', + mock.Mock(return_value={ + "path": "fake_path", + "cifsname": "fake_cifsname", + "protocol": "fake_protocol", + "roList": ["fake_ro"], + "rwList": ["fake_rw"], + "allowList": ["fake_allow"], + "denyList": ["fake_deny"], + })) + self.driver.helper.configuration.macrosan_nas_ip = "172.0.0.1" + locations = self.driver.ensure_share(self._context, share) + expect_share_path = self.driver.helper._generate_share_path( + 'manila_fakeid') + if share_proto == 'nfs': + expect_locations = [r'172.0.0.1:/manila_fakeid/manila_fakeid'] + self.assertEqual(locations, expect_locations) + mock_gns.assert_called_once_with(expect_share_path) + else: + expect_locations = [r'\\172.0.0.1\manila_fakeid'] + self.assertEqual(locations, expect_locations) + mock_gcs.assert_called_once_with(expect_share_path) + + def test_ensure_share_proto_fail(self): + share = fake_share.fake_share(host="fake_host@fake_backend#fake_pool") + self.mock_object(rest_helper.RestHelper, '_get_nfs_share', + mock.Mock(return_value={ + "path": "fake_path", + "clients": ["client"], + "protocol": "fake_protocol" + })) + self.assertRaises(exception.MacrosanBackendExeption, + self.driver.ensure_share, + self._context, + share) + + def test_ensure_share_not_exist(self): + share = fake_share.fake_share(share_proto='nfs', + host="fake_host@fake_backend#fake_pool") + mock_gns = self.mock_object(rest_helper.RestHelper, '_get_nfs_share', + mock.Mock(return_value=None)) + self.assertRaises(exception.ShareResourceNotFound, + self.driver.ensure_share, + self._context, + share) + expect_share_path = self.driver.helper._generate_share_path( + 'manila_fakeid') + mock_gns.assert_called_once_with(expect_share_path) + + @ddt.data('nfs', 'cifs') + def test_allow_access_success(self, share_proto): + share = fake_share.fake_share(share_proto=share_proto, + host="fake_host@fake_backend#fake_pool") + if share_proto == 'nfs': + access = { + 'access_type': 'ip', + 'access_to': '0.0.0.0/0', + 'access_level': 'rw', + } + else: + access = { + 'access_type': 'user', + 'access_to': 'fake_user', + 'access_level': 'rw', + } + mock_gns = self.mock_object(rest_helper.RestHelper, + '_get_nfs_share', + mock.Mock(return_value={ + "path": "fake_path", + "clients": ["client"], + "protocol": "fake_protocol" + })) + mock_gafns = self.mock_object(rest_helper.RestHelper, + '_get_access_from_nfs_share', + mock.Mock(return_value=None)) + mock_anar = self.mock_object(rest_helper.RestHelper, + '_allow_nfs_access_rest') + mock_gcs = self.mock_object(rest_helper.RestHelper, + '_get_cifs_share', + mock.Mock(return_value={ + "path": "fake_path", + "cifsname": "fake_cifsname", + "protocol": "fake_protocol", + "roList": ["fake_ro"], + "rwList": ["fake_rw"], + "allowList": ["fake_allow"], + "denyList": ["fake_deny"], + })) + mock_gafcs = self.mock_object(rest_helper.RestHelper, + '_get_access_from_cifs_share', + mock.Mock(return_value=None)) + mock_acar = self.mock_object(rest_helper.RestHelper, + '_allow_cifs_access_rest') + self.driver.helper._allow_access(share, access) + expect_share_path = self.driver.helper._generate_share_path( + 'manila_fakeid') + if access['access_to'] == '0.0.0.0/0': + access['access_to'] = '*' + if share_proto == 'nfs': + mock_gns.assert_called_once_with(expect_share_path) + mock_gafns.assert_called_once_with(expect_share_path, + access['access_to']) + mock_anar.assert_called_once_with(expect_share_path, + access['access_to'], + access['access_level']) + else: + mock_gcs.assert_called_once_with(expect_share_path) + mock_gafcs.assert_called_once_with(expect_share_path, + access['access_to']) + mock_acar.assert_called_once_with(expect_share_path, + access['access_to'], + access['access_level']) + + def test_allow_access_nfs_change(self): + share = fake_share.fake_share(share_proto='nfs', + host="fake_host@fake_backend#fake_pool") + access = { + 'access_type': 'ip', + 'access_to': '172.0.0.1', + 'access_level': 'rw', + } + mock_gns = self.mock_object(rest_helper.RestHelper, '_get_nfs_share', + mock.Mock(return_value={ + "path": "/manila_fakeid", + "clients": ["client"], + "protocol": "fake_protocol" + })) + mock_gafns = self.mock_object(rest_helper.RestHelper, + '_get_access_from_nfs_share', + mock.Mock(return_value={ + "path": "/manila_fakeid", + "clientName": "fake_client_name", + "accessRight": "ro", + })) + mock_cnar = self.mock_object(rest_helper.RestHelper, + '_change_nfs_access_rest') + self.driver.helper._allow_access(share, access) + expect_share_path = self.driver.helper._generate_share_path( + 'manila_fakeid') + mock_gns.assert_called_once_with(expect_share_path) + mock_gafns.assert_called_once_with(expect_share_path, + access['access_to']) + mock_cnar.assert_called_once_with(expect_share_path, + access['access_to'], + access['access_level']) + + def test_allow_access_cifs_change(self): + share = fake_share.fake_share(share_proto='cifs', + host="fake_host@fake_backend#fake_pool") + access = { + 'access_type': 'user', + 'access_to': 'fake_user', + 'access_level': 'rw', + } + mock_gcs = self.mock_object(rest_helper.RestHelper, + '_get_cifs_share', + mock.Mock(return_value={ + "path": "fake_path", + "cifsname": "fake_cifsname", + "protocol": "fake_protocol", + "roList": ["fake_ro"], + "rwList": ["fake_rw"], + "allowList": ["fake_allow"], + "denyList": ["fake_deny"], + })) + mock_gafcs = self.mock_object(rest_helper.RestHelper, + '_get_access_from_cifs_share', + mock.Mock(return_value={ + "path": "fake_path", + "ugName": "fake_user", + "ugType": "0", + "accessRight": "ro", + })) + mock_ccar = self.mock_object(rest_helper.RestHelper, + '_change_cifs_access_rest') + self.driver.helper._allow_access(share, access) + expect_share_path = self.driver.helper._generate_share_path( + 'manila_fakeid') + mock_gcs.assert_called_once_with(expect_share_path) + mock_gafcs.assert_called_once_with(expect_share_path, + access['access_to']) + mock_ccar.assert_called_once_with(expect_share_path, + access['access_to'], + access['access_level'], + '0') + + @ddt.data( + { + 'access_type': 'user', + 'access_to': 'user_name', + 'access_level': 'rw', + }, + { + 'access_type': 'user', + 'access_to': 'group_name', + 'access_level': 'rw', + }, + { + 'access_type': 'user', + 'access_to': '/domain_user', + 'access_level': 'rw', + }, + { + 'access_type': 'user', + 'access_to': '/domain_group', + 'access_level': 'rw', + }, + ) + def test_allow_access_cifs(self, access): + share = fake_share.fake_share(share_proto='cifs', + host="fake_host@fake_backend#fake_pool") + mock_gcs = self.mock_object(rest_helper.RestHelper, + '_get_cifs_share', + mock.Mock(return_value={ + "path": "fake_path", + "cifsname": "fake_cifsname", + "protocol": "fake_protocol", + "roList": ["fake_ro"], + "rwList": ["fake_rw"], + "allowList": ["fake_allow"], + "denyList": ["fake_deny"], + })) + mock_gafcs = self.mock_object(rest_helper.RestHelper, + '_get_access_from_cifs_share', + mock.Mock(return_value=None)) + mock_acar = self.mock_object(rest_helper.RestHelper, + '_allow_cifs_access_rest') + + self.driver.helper._allow_access(share, access) + expect_share_path = self.driver.helper._generate_share_path( + 'manila_fakeid') + mock_gcs.assert_called_once_with(expect_share_path) + mock_gafcs.assert_called_once_with(expect_share_path, + access['access_to']) + mock_acar.assert_called_once_with(expect_share_path, + access['access_to'], + access['access_level']) + + @ddt.data('nfs', 'cifs') + def test_allow_access_share_not_exist(self, share_proto): + share = fake_share.fake_share(share_proto=share_proto, + host="fake_host@fake_backend#fake_pool") + access = {} + if share_proto == 'nfs': + access = { + 'access_type': 'ip', + 'access_to': '172.0.0.1', + 'access_level': 'rw', + } + else: + access = { + 'access_type': 'user', + 'access_to': 'fake_user', + 'access_level': 'rw', + } + mock_gns = self.mock_object(rest_helper.RestHelper, '_get_nfs_share', + mock.Mock(return_value=None)) + mock_gcs = self.mock_object(rest_helper.RestHelper, '_get_cifs_share', + mock.Mock(return_value=None)) + self.assertRaises(exception.ShareResourceNotFound, + self.driver.helper._allow_access, + share, + access) + expect_share_path = self.driver.helper._generate_share_path( + 'manila_fakeid') + if share_proto == 'nfs': + mock_gns.assert_called_once_with(expect_share_path) + else: + mock_gcs.assert_called_once_with(expect_share_path) + + def test_allow_access_proto_fail(self): + share = fake_share.fake_share(host="fake_host@fake_backend#fake_pool") + access = { + 'access_type': 'user', + 'access_to': 'fake_user', + 'access_level': 'rw', + } + self.assertRaises(exception.MacrosanBackendExeption, + self.driver.helper._allow_access, + share, + access) + + def test_allow_access_nfs_user_fail(self): + share = fake_share.fake_share(share_proto='nfs', + host="fake_host@fake_backend#fake_pool") + access = { + 'access_type': 'user', + 'access_to': 'fake_user', + 'access_level': 'rw', + } + self.assertRaises(exception.InvalidShareAccess, + self.driver.helper._allow_access, + share, + access) + + def test_allow_access_cifs_ip_fail(self): + share = fake_share.fake_share(share_proto='cifs', + host="fake_host@fake_backend#fake_pool") + access = { + 'access_type': 'ip', + 'access_to': '172.0.0.1', + 'access_level': 'rw', + } + self.assertRaises(exception.InvalidShareAccess, + self.driver.helper._allow_access, + share, + access) + + def test_allow_access_nfs_level_fail(self): + share = fake_share.fake_share(share_proto='nfs', + host="fake_host@fake_backend#fake_pool") + access = { + 'access_type': 'ip', + 'access_to': '172.0.0.1', + 'access_level': 'r', + } + self.assertRaises(exception.InvalidShareAccess, + self.driver.helper._allow_access, + share, + access) + + @ddt.data('nfs', 'cifs') + def test_deny_access(self, share_proto): + share = fake_share.fake_share(share_proto=share_proto, + host="fake_host@fake_backend#fake_pool") + if share_proto == 'nfs': + access = { + 'access_type': 'ip', + 'access_to': '0.0.0.0/0', + 'access_level': 'rw', + } + else: + access = { + 'access_type': 'user', + 'access_to': 'fake_user', + 'access_level': 'rw', + } + mock_gafns = self.mock_object(rest_helper.RestHelper, + '_get_access_from_nfs_share', + mock.Mock(return_value={ + "path": "fake_path", + "clientName": "fake_client_name", + "accessRight": "rw", + })) + mock_dnar = self.mock_object(rest_helper.RestHelper, + '_delete_nfs_access_rest') + mock_gafcs = self.mock_object(rest_helper.RestHelper, + '_get_access_from_cifs_share', + mock.Mock(return_value={ + "path": "fake_path", + "ugName": "fake_user", + "ugType": "0", + "accessRight": "rw", + })) + mock_dcar = self.mock_object(rest_helper.RestHelper, + '_delete_cifs_access_rest') + expect_share_path = self.driver.helper._generate_share_path( + 'manila_fakeid') + self.driver.helper._deny_access(share, access) + if access['access_to'] == '0.0.0.0/0': + access['access_to'] = '*' + if share_proto == 'nfs': + mock_gafns.assert_called_once_with(expect_share_path, + access['access_to']) + mock_dnar.assert_called_once_with(expect_share_path, + access['access_to']) + else: + mock_gafcs.assert_called_once_with(expect_share_path, + access['access_to']) + mock_dcar.assert_called_once_with(expect_share_path, + "fake_user", "0") + + def test_deny_access_nfs_type_fail(self): + share = fake_share.fake_share(share_proto='nfs', + host="fake_host@fake_backend#fake_pool") + access = { + 'access_type': 'fake_type', + 'access_to': '172.0.0.1', + 'access_level': 'rw', + } + result = self.driver.helper._deny_access(share, access) + self.assertIsNone(result) + + def test_deny_access_nfs_share_not_exist(self): + share = fake_share.fake_share(share_proto='nfs', + host="fake_host@fake_backend#fake_pool") + access = { + 'access_type': 'ip', + 'access_to': '172.0.0.1', + 'access_level': 'rw', + } + mock_gafns = self.mock_object(rest_helper.RestHelper, + '_get_access_from_nfs_share', + mock.Mock(return_value=None)) + result = self.driver.helper._deny_access(share, access) + self.assertIsNone(result) + expect_share_path = self.driver.helper._generate_share_path( + 'manila_fakeid') + mock_gafns.assert_called_once_with(expect_share_path, + access['access_to']) + + def test_deny_access_cifs_type_fail(self): + share = fake_share.fake_share(share_proto='cifs', + host="fake_host@fake_backend#fake_pool") + access = { + 'access_type': 'fake_type', + 'access_to': 'fake_user', + 'access_level': 'rw', + } + result = self.driver.helper._deny_access(share, access) + self.assertIsNone(result) + + def test_deny_access_cifs_share_not_exist(self): + share = fake_share.fake_share(share_proto='cifs', + host="fake_host@fake_backend#fake_pool") + access = { + 'access_type': 'user', + 'access_to': 'fake_user', + 'access_level': 'rw', + } + mock_gafcs = self.mock_object(rest_helper.RestHelper, + '_get_access_from_cifs_share', + mock.Mock(return_value=None)) + result = self.driver.helper._deny_access(share, access) + self.assertIsNone(result) + expect_share_path = self.driver.helper._generate_share_path( + 'manila_fakeid') + mock_gafcs.assert_called_once_with(expect_share_path, + access['access_to']) + + def test_update_access_add_delete(self): + share = fake_share.fake_share(share_proto='nfs', + host="fake_host@fake_backend#fake_pool") + add_rules = [{'access_type': 'ip', + 'access_to': '172.0.2.1', + 'access_level': 'rw', }] + delete_rules = [{'access_type': 'ip', + 'access_to': '172.0.2.2', + 'access_level': 'rw', }] + self.mock_object(macrosan_helper.MacrosanHelper, + '_allow_access') + self.mock_object(macrosan_helper.MacrosanHelper, + '_deny_access') + self.driver.update_access(self._context, share, + None, add_rules, delete_rules) + + @ddt.data('nfs', 'cifs') + def test_update_access_nfs(self, proto): + share = fake_share.fake_share(share_proto=proto, + host="fake_host@fake_backend#fake_pool") + if proto == 'nfs': + access_rules = [{'access_type': 'ip', + 'access_to': '172.0.3.1', + 'access_level': 'rw', }, + {'access_type': 'ip', + 'access_to': '172.0.3.2', + 'access_level': 'rw', }] + else: + access_rules = [{'access_type': 'user', + 'access_to': 'user_l', + 'access_level': 'rw', }, + {'access_type': 'user', + 'access_to': 'user_a', + 'access_level': 'rw', }] + mock_ca = self.mock_object(macrosan_helper.MacrosanHelper, + '_clear_access') + self.mock_object(macrosan_helper.MacrosanHelper, + '_allow_access') + self.driver.update_access(self._context, share, + access_rules, {}, {}) + mock_ca.assert_called_once_with(share, None) + + def test_update_access_fail(self): + share = fake_share.fake_share(share_proto='nfs', + host="fake_host@fake_backend#fake_pool") + access_rules = [{'access_id': 'fakeid', + 'access_type': 'ip', + 'access_to': '172.0.3.1', + 'access_level': 'rw', }] + mock_ca = self.mock_object(macrosan_helper.MacrosanHelper, + '_clear_access') + self.mock_object(macrosan_helper.MacrosanHelper, + '_allow_access', + mock.Mock(side_effect=exception.InvalidShareAccess( + reason='fake_exception'))) + result = self.driver.update_access(self._context, share, + access_rules, None, None) + expect = { + 'fakeid': { + 'state': 'error', + } + } + self.assertEqual(result, expect) + mock_ca.assert_called_once_with(share, None) + + def test_update_access_add_fail(self): + share = fake_share.fake_share(share_proto='nfs', + host="fake_host@fake_backend#fake_pool") + add_rules = [{'access_id': 'fakeid', + 'access_type': 'ip', + 'access_to': '172.0.2.1', + 'access_level': 'rw', }] + delete_rules = [] + self.mock_object(macrosan_helper.MacrosanHelper, + '_allow_access', + mock.Mock(side_effect=exception.InvalidShareAccess( + reason='fake_exception'))) + self.mock_object(macrosan_helper.MacrosanHelper, + '_deny_access') + result = self.driver.update_access(self._context, share, + None, add_rules, delete_rules) + expect = { + 'fakeid': { + 'state': 'error' + } + } + self.assertEqual(result, expect) + + @ddt.data('nfs', 'cifs') + def test__clear_access(self, share_proto): + share = fake_share.fake_share(share_proto=share_proto, + host="fake_host@fake_backend#fake_pool") + fake_nfs_share_backend = [ + { + 'share_path': 'fake_path', + 'access_to': '172.0.0.1', + 'access_level': 'rw' + }, + { + 'share_path': 'default_path', + 'access_to': '172.0.0.2', + 'access_level': 'rw' + }] + fake_cifs_share_backend = [ + { + 'share_path': 'fake_path', + 'access_to': 'user_name', + 'ugType': '0', + 'access_level': 'rw' + }, + { + 'share_path': 'default_path', + 'access_to': 'manilanobody', + 'ugType': '0', + 'access_level': 'rw' + }] + mock_ganar = self.mock_object( + rest_helper.RestHelper, + '_get_all_nfs_access_rest', + mock.Mock(return_value=fake_nfs_share_backend)) + mock_gacar = self.mock_object( + rest_helper.RestHelper, '_get_all_cifs_access_rest', + mock.Mock(return_value=fake_cifs_share_backend)) + self.mock_object(rest_helper.RestHelper, + '_delete_nfs_access_rest') + self.mock_object(rest_helper.RestHelper, + '_delete_cifs_access_rest') + self.driver.helper._clear_access(share) + expect_share_path = self.driver.helper._generate_share_path( + 'manila_fakeid') + if share_proto == 'nfs': + mock_ganar.assert_called_once_with(expect_share_path) + else: + mock_gacar.assert_called_once_with(expect_share_path) + + @ddt.data('nfs', 'cifs') + def test__clear_access_no_access_list(self, share_proto): + share = fake_share.fake_share(share_proto=share_proto, + host="fake_host@fake_backend#fake_pool") + mock_ganar = self.mock_object( + rest_helper.RestHelper, + '_get_all_nfs_access_rest', + mock.Mock(return_value=[])) + mock_gacar = self.mock_object( + rest_helper.RestHelper, '_get_all_cifs_access_rest', + mock.Mock(return_value=[])) + self.driver.helper._clear_access(share) + expect_share_path = self.driver.helper._generate_share_path( + 'manila_fakeid') + if share_proto == 'nfs': + mock_ganar.assert_called_once_with(expect_share_path) + else: + mock_gacar.assert_called_once_with(expect_share_path) + + @ddt.data(constants.USER_NOT_EXIST, + constants.USER_EXIST, + constants.USER_FORMAT_ERROR) + def test__ensure_user(self, query_result): + mock_qu = self.mock_object(rest_helper.RestHelper, + '_query_user', + mock.Mock(return_value=query_result)) + mock_qg = self.mock_object( + rest_helper.RestHelper, + '_query_group', + mock.Mock(return_value=constants.GROUP_NOT_EXIST)) + mock_alg = self.mock_object(rest_helper.RestHelper, + '_add_localgroup') + mock_alu = self.mock_object(rest_helper.RestHelper, + '_add_localuser') + result = self.driver.helper._ensure_user('fake_user', + 'fake_passwd', + 'fake_group') + if query_result == constants.USER_NOT_EXIST: + mock_qg.assert_called_once_with('fake_group') + mock_alg.assert_called_once_with('fake_group') + mock_alu.assert_called_once_with('fake_user', + 'fake_passwd', + 'fake_group') + self.assertTrue(result) + elif query_result == constants.USER_EXIST: + self.assertTrue(result) + else: + self.assertFalse(result) + mock_qu.assert_called_once_with('fake_user') + + def test__ensure_user_fail(self): + mock_qu = self.mock_object( + rest_helper.RestHelper, + '_query_user', + mock.Mock(return_value=constants.USER_NOT_EXIST)) + mock_qg = self.mock_object( + rest_helper.RestHelper, + '_query_group', + mock.Mock(return_value=constants.GROUP_FORMAT_ERROR)) + self.assertRaises(exception.InvalidInput, + self.driver.helper._ensure_user, + 'fake_user', + 'fake_passwd', + 'fake_group') + mock_qu.assert_called_once_with('fake_user') + mock_qg.assert_called_once_with('fake_group') + + def test__update_share_stats(self): + self.driver.helper.pools = ['fake_pool'] + mock_gap = self.mock_object(rest_helper.RestHelper, + '_get_all_pool', + mock.Mock(return_value='fake_result')) + mock_gpc = self.mock_object(macrosan_helper.MacrosanHelper, + '_get_pool_capacity', + mock.Mock(return_value={ + "totalcapacity": 10, + "freecapacity": 9, + "allocatedcapacity": 1, + })) + mock_uss = self.mock_object(driver.ShareDriver, '_update_share_stats') + + self.driver._update_share_stats() + + data = {} + data['vendor_name'] = self.driver.VENDOR + data['driver_version'] = self.driver.VERSION + data['storage_protocol'] = self.driver.PROTOCOL + data['share_backend_name'] = 'fake_share_backend_name' + data['pools'] = [{ + 'pool_name': 'fake_pool', + 'total_capacity_gb': 10, + 'free_capacity_gb': 9, + 'allocated_capacity_gb': 1, + 'reserved_percentage': 0, + 'reserved_snapshot_percentage': 0, + 'reserved_share_extend_percentage': 0, + 'dedupe': False, + 'compression': False, + 'qos': False, + 'thin_provisioning': False, + 'snapshot_support': False, + 'create_share_from_snapshot_support': + False, + }] + mock_gap.assert_called_once() + mock_gpc.assert_called_once_with('fake_pool', 'fake_result') + mock_uss.assert_called_once_with(data) + + def test__update_share_stats_pool_not_exist(self): + self.driver.helper.pools = ['fake_pool'] + self.mock_object(rest_helper.RestHelper, '_get_all_pool', + mock.Mock(return_value='fake_result')) + self.mock_object(macrosan_helper.MacrosanHelper, + '_get_pool_capacity', + mock.Mock(return_value={})) + self.assertRaises(exception.InvalidInput, + self.driver._update_share_stats + ) + + def test__get_pool_capacity(self): + self.mock_object(macrosan_helper.MacrosanHelper, + '_find_pool_info', + mock.Mock(return_value={ + "name": "fake_pool", + "totalcapacity": "100.0G", + "allocatedcapacity": "22G", + "freecapacity": "78G", + "health": "ONLINE", + "rw": "off", + })) + res = self.driver.helper._get_pool_capacity("fake_pool", + "fake_result") + self.assertEqual(100, res['totalcapacity']) + self.assertEqual(78, res['freecapacity']) + self.assertEqual(22, res['allocatedcapacity']) + + def test__generate_share_name(self): + share = fake_share.fake_share(host="fake_host@fake_backend#fake_pool") + result = self.driver.helper._generate_share_name(share) + self.assertEqual("manila_fakeid", result) + + def test__format_name(self): + a = 'fake-1234567890-1234567890-1234567890' + expect = 'fake_1234567890_1234567890_1234' + result = self.driver.helper._format_name(a) + self.assertEqual(expect, result) + + def test__generate_share_path(self): + share_name = 'manila_fakeid' + result = self.driver.helper._generate_share_path(share_name) + + self.assertEqual(r'/manila_fakeid/manila_fakeid', result) + + @ddt.data('nfs', 'cifs') + def test__get_location_path(self, share_proto): + self.driver.helper.configuration.macrosan_nas_ip = "172.0.0.1" + result = self.driver.helper._get_location_path('fake_path', + 'fake_name', + share_proto) + if share_proto == 'nfs': + expect = r'172.0.0.1:fake_path' + elif share_proto == 'cifs': + expect = r'\\172.0.0.1\fake_name' + self.assertEqual(expect, result) + + def test__get_share_instance_pnp_pool_error(self): + share = fake_share.fake_share( + share_proto="nfs", host="fake_host@fake_backend") + self.assertRaises(exception.InvalidHost, + self.driver.helper._get_share_instance_pnp, + share) + + def test__get_share_instance_pnp_proto_error(self): + share = fake_share.fake_share( + share_proto="CephFS", host="fake_host@fake_backend#fake_pool") + self.assertRaises(exception.MacrosanBackendExeption, + self.driver.helper._get_share_instance_pnp, + share) + + @ddt.data('2000000000', '2000000KB', '2000MB', '20GB', '2TB') + def test__unit_convert_toGB(self, capacity): + convert = {'2000000000': '%.0f' % (float(2000000000) / 1024 ** 3), + '2000000KB': '%.0f' % (float(2000000) / 1024 ** 2), + '2000MB': '%.0f' % (float(2000) / 1024), + '20GB': '%.0f' % float(20), + '2TB': '%.0f' % (float(2) * 1024)} + expect = float(convert[capacity]) + result = self.driver.helper._unit_convert_toGB(capacity) + self.assertEqual(expect, result) + + @ddt.data('nfs', 'cifs') + def test__get_share(self, proto): + proto = proto.upper() + mock_gns = self.mock_object(rest_helper.RestHelper, + '_get_nfs_share', + mock.Mock(return_value={ + "path": "/manila_fakeid", + "clients": ["client"], + "protocol": "NFS" + })) + mock_gcs = self.mock_object(rest_helper.RestHelper, + '_get_cifs_share', + mock.Mock(return_value={ + "path": "fake_path", + "cifsname": "fake_cifsname", + "protocol": "CIFS", + "roList": ["fake_ro"], + "rwList": ["fake_rw"], + "allowList": ["fake_allow"], + "denyList": ["fake_deny"], + })) + expect_nfs = { + "path": "/manila_fakeid", + "clients": ["client"], + "protocol": "NFS"} + expect_cifs = { + "path": "fake_path", + "cifsname": "fake_cifsname", + "protocol": "CIFS", + "roList": ["fake_ro"], + "rwList": ["fake_rw"], + "allowList": ["fake_allow"], + "denyList": ["fake_deny"]} + result = self.driver.helper._get_share('fake_path', proto) + if proto == 'NFS': + mock_gns.assert_called_once_with('fake_path') + self.assertEqual(expect_nfs, result) + elif proto == 'CIFS': + mock_gcs.assert_called_once_with('fake_path') + self.assertEqual(expect_cifs, result) + + def test__find_pool_info(self): + pool_info = self.driver.helper._find_pool_info( + 'fake_pool', + self.result_success_storage_pools) + self.assertIsNotNone(pool_info) + + def test__find_pool_info_fail(self): + pool_info = self.driver.helper._find_pool_info( + 'error_pool', + self.result_success_storage_pools) + expect = {} + self.assertEqual(expect, pool_info) + + +@ddt.ddt +class RestHelperTestCase(test.TestCase): + + def setUp(self): + self.mock_object(CONF, '_check_required_opts') + super(RestHelperTestCase, self).setUp() + + def _safe_get(opt): + return getattr(self.configuration, opt) + + self.configuration = mock.Mock(spec=configuration.Configuration) + self.configuration.safe_get = mock.Mock(side_effect=_safe_get) + self.configuration.macrosan_nas_http_protocol = 'https' + self.configuration.macrosan_nas_ip = 'fake_ip' + self.configuration.macrosan_nas_port = 'fake_port' + self.configuration.macrosan_nas_prefix = 'nas' + self.configuration.macrosan_nas_username = 'fake_username' + self.configuration.macrosan_nas_password = 'fake_password' + self.configuration.macrosan_timeout = 60 + self.configuration.macrosan_ssl_cert_verify = False + self.resthelper = rest_helper.RestHelper( + configuration=self.configuration) + self.post = 'POST' + self.get = 'GET' + self.delete = 'DELETE' + self.put = 'PUT' + self.fake_message = 'fake_message' + self.result_success = { + 'code': 0, + 'message': 'success', + 'data': 'fake_data' + } + self.result_success_return_0 = { + 'code': 0, + 'message': 'success', + 'data': '0' + } + self.result_success_return_1 = { + 'code': 0, + 'message': 'success', + 'data': '1' + } + self.result_failed = { + 'code': 1, + 'message': 'failed', + 'data': 'fake_data' + } + self.result_failed_not_exist = { + 'code': constants.CODE_SOURCE_NOT_EXIST, + 'message': 'failed', + 'data': '', + } + self.result_success_storage_pools = { + 'code': 0, + 'message': 'success', + 'data': [{ + 'name': 'fake_pool', + 'size': '1000.0G', + 'allocated': '100G', + 'free': '900G', + 'health': 'ONLINE', + 'rwStatus': 'off' + }] + } + + @ddt.data( + {'url': 'fake_url', 'data': {'fake_data': 'fake_value'}, + 'method': 'POST'}, + {'url': 'fake_url', 'data': None, + 'method': 'GET'}, + {'url': 'fake_url', 'data': {'fake_data': 'fake_value'}, + 'method': 'DELETE'}, + {'url': 'fake_url', 'data': {'fake_data': 'fake_value'}, + 'method': 'PUT'}, + ) + @ddt.unpack + def test_call(self, url, data, method): + self.resthelper._token = 'fake_token' + request_method = method.lower() + fake_response = FakeResponse(200, self.result_success) + mock_request = self.mock_object(requests, request_method, + mock.Mock(return_value=fake_response)) + self.resthelper.call(url, data, method) + expected_url = ('https://%(ip)s:%(port)s/%(rest)s/%(url)s' + % {'ip': 'fake_ip', + 'port': 'fake_port', + 'rest': 'nas', + 'url': 'fake_url'}) + header = {'Authorization': 'fake_token'} + mock_request.assert_called_once_with( + expected_url, data=data, headers=header, + timeout=self.configuration.macrosan_timeout, + verify=False) + + def test_call_method_fail(self): + self.resthelper._token = 'fake_token' + self.assertRaises(exception.ShareBackendException, + self.resthelper.call, + 'fake_url', + 'fake_data', + 'error_method') + + def test_call_token_fail(self): + self.resthelper._token = 'fake_token' + fake_result_fail = { + 'code': 302, + 'message': 'fake_message', + 'data': 'fake_data' + } + self.mock_object(self.resthelper, 'do_request', + mock.Mock(return_value=fake_result_fail)) + self.assertRaises(exception.MacrosanBackendExeption, + self.resthelper.call, + 'fake_url', + 'fake_data', + self.post) + + def test_call_token_none(self): + self.resthelper._token = None + self.mock_object(self.resthelper, 'do_request', + mock.Mock(return_value=self.result_success)) + mock_l = self.mock_object(self.resthelper, 'login', + mock.Mock(return_value='fake_token')) + self.resthelper.call('fake_url', 'fake_data', self.post) + mock_l.assert_called_once() + + def test_call_token_expired(self): + self.resthelper._token = 'fake_token' + fake_result = { + 'code': 301, + 'message': 'token expired', + 'data': 'fake_data' + } + self.mock_object( + self.resthelper, 'do_request', + mock.Mock(side_effect=[fake_result, self.result_success])) + mock_l = self.mock_object(self.resthelper, 'login', + mock.Mock(return_value='fake_token')) + self.resthelper.call('fake_url', 'fake_data', self.post) + mock_l.assert_called_once() + + def test_call_fail(self): + self.resthelper._token = 'fake_token' + fake_response = FakeResponse(302, self.result_success) + self.mock_object(requests, 'post', + mock.Mock(return_value=fake_response)) + self.assertRaises(exception.NetworkException, + self.resthelper.call, + 'fake_url', + 'fake_data', + self.post) + + def test_login(self): + fake_result = { + 'code': 0, + 'message': 'Login success', + 'data': 'fake_token' + } + mock_rd = self.mock_object(self.resthelper, 'do_request', + mock.Mock(return_value=fake_result)) + self.resthelper.login() + login_data = {'userName': self.configuration.macrosan_nas_username, + 'userPasswd': self.configuration.macrosan_nas_password} + mock_rd.assert_called_once_with('rest/token', login_data, + self.post) + self.assertEqual('fake_token', self.resthelper._token) + + def test_login_fail(self): + mock_rd = self.mock_object(self.resthelper, 'do_request', + mock.Mock(return_value=self.result_failed)) + + self.assertRaises(exception.ShareBackendException, + self.resthelper.login) + login_data = {'userName': self.configuration.macrosan_nas_username, + 'userPasswd': self.configuration.macrosan_nas_password} + mock_rd.assert_called_once_with('rest/token', login_data, + self.post) + + def test__assert_result_code(self): + self.resthelper._assert_result_code(self.result_success, + self.fake_message) + + def test__assert_result_code_fail(self): + self.assertRaises(exception.ShareBackendException, + self.resthelper._assert_result_code, + self.result_failed, + self.fake_message) + + def test__assert_result_data(self): + self.resthelper._assert_result_data(self.result_success, + self.fake_message) + + def test__assert_result_data_fail(self): + fake_result = { + 'code': 0, + 'message': 'fake_message' + } + self.assertRaises(exception.ShareBackendException, + self.resthelper._assert_result_data, + fake_result, + self.fake_message) + + def test__create_nfs_share(self): + mock_call = self.mock_object(self.resthelper, + 'call') + self.mock_object(self.resthelper, + '_assert_result_code') + self.resthelper._create_nfs_share('fake_path') + url = 'rest/nfsShare' + data = { + 'path': 'fake_path', + 'authority': 'ro', + 'accessClient': '192.0.2.0', + } + mock_call.assert_called_once_with(url, data, self.post) + + def test__get_nfs_share(self): + fake_result = { + 'code': 0, + 'message': 'success', + 'data': { + "path": "fake_path", + "clients": ["client"], + "protocol": "fake_protocol" + } + } + mock_call = self.mock_object(self.resthelper, + 'call', + mock.Mock(return_value=fake_result)) + self.mock_object(self.resthelper, + '_assert_result_code') + result = self.resthelper._get_nfs_share('fake_path') + expect = { + "path": "fake_path", + "clients": ["client"], + "protocol": "fake_protocol" + } + self.assertEqual(expect, result) + url = 'rest/nfsShare?path=fake_path' + mock_call.assert_called_once_with(url, None, self.get) + + def test__delete_nfs_share(self): + mock_call = self.mock_object(self.resthelper, + 'call') + self.mock_object(self.resthelper, + '_assert_result_code') + self.resthelper._delete_nfs_share('fake_path') + url = 'rest/nfsShare?path=fake_path' + mock_call.assert_called_once_with(url, None, self.delete) + + def test__create_cifs_share(self): + mock_call = self.mock_object(self.resthelper, + 'call') + self.mock_object(self.resthelper, + '_assert_result_code') + self.resthelper._create_cifs_share('fake_name', + 'fake_path', + ['fake_user'], + ['0']) + url = 'rest/cifsShare' + data = { + 'path': 'fake_path', + 'cifsName': 'fake_name', + 'cifsDescription': '', + 'RoList': [], + 'RoListType': [], + 'RwList': ['fake_user'], + 'RwListType': ['0'], + 'allowList': [], + 'denyList': [], + } + mock_call.assert_called_once_with(url, data, self.post) + + def test__get_cifs_share(self): + fake_result = { + 'code': 0, + 'message': 'success', + 'data': { + "path": "fake_path", + "cifsname": "fake_cifsname", + "protocol": "fake_protocol", + "roList": ["fake_ro"], + "rwList": ["fake_rw"], + "allowList": ["fake_allow"], + "denyList": ["fake_deny"] + } + } + mock_call = self.mock_object(self.resthelper, + 'call', + mock.Mock(return_value=fake_result)) + self.mock_object(self.resthelper, + '_assert_result_code') + result = self.resthelper._get_cifs_share('fake_path') + expect = { + "path": "fake_path", + "cifsname": "fake_cifsname", + "protocol": "fake_protocol", + "roList": ["fake_ro"], + "rwList": ["fake_rw"], + "allowList": ["fake_allow"], + "denyList": ["fake_deny"] + } + self.assertEqual(expect, result) + url = 'rest/cifsShare?path=fake_path' + mock_call.assert_called_once_with(url, None, self.get) + + def test__delete_cifs_share(self): + mock_call = self.mock_object(self.resthelper, + 'call') + self.mock_object(self.resthelper, + '_assert_result_code') + self.resthelper._delete_cifs_share('fake_name', 'fake_path') + url = 'rest/cifsShare?path=fake_path&cifsName=fake_name' + mock_call.assert_called_once_with(url, None, self.delete) + + def test__update_share_size(self): + mock_call = self.mock_object(self.resthelper, + 'call') + self.mock_object(self.resthelper, + '_assert_result_code') + self.resthelper._update_share_size('fake_filesystem', '2GB') + url = 'rest/filesystem/fake_filesystem' + data = { + 'capacity': '2GB', + } + mock_call.assert_called_once_with(url, data, self.put) + + def test___create_filesystem(self): + mock_call = self.mock_object(self.resthelper, + 'call') + self.mock_object(self.resthelper, + '_assert_result_code') + self.resthelper._create_filesystem('fake_filesystem', + 'fake_pool', + '1GB') + url = 'rest/filesystem' + data = { + 'fsName': 'fake_filesystem', + 'poolName': 'fake_pool', + 'createType': '0', + 'fileSystemQuota': '1GB', + 'fileSystemReserve': '1GB', + 'wormStatus': 0, + 'defaultTimeStatus': 0, + 'defaultTimeNum': 0, + 'defaultTimeUnit': 'year', + 'isAutoLock': 0, + 'isAutoDelete': 0, + 'lockTime': 0 + } + mock_call.assert_called_once_with(url, data, self.post) + + def test__delete_filesystem(self): + mock_call = self.mock_object(self.resthelper, + 'call') + self.mock_object(self.resthelper, + '_assert_result_code') + self.resthelper._delete_filesystem('fake_filesystem') + url = 'rest/filesystem/fake_filesystem' + mock_call.assert_called_once_with(url, None, self.delete) + + def test__get_filesystem(self): + fake_result = { + 'code': 0, + 'message': 'success', + 'data': { + 'name': 'fake_filesystem', + 'poolName': 'fake_pool', + } + } + mock_call = self.mock_object(self.resthelper, + 'call', + mock.Mock(return_value=fake_result)) + self.mock_object(self.resthelper, + '_assert_result_code') + result = self.resthelper._get_filesystem('fake_filesystem') + expect = { + 'name': 'fake_filesystem', + 'poolName': 'fake_pool', + } + self.assertEqual(expect, result) + url = 'rest/filesystem/fake_filesystem' + mock_call.assert_called_once_with(url, None, self.get) + + def test__create_filesystem_dir(self): + mock_call = self.mock_object(self.resthelper, + 'call') + self.mock_object(self.resthelper, + '_assert_result_code') + self.resthelper._create_filesystem_dir('/fake_path/fake_dir') + url = 'rest/fileDir' + data = { + 'path': '/fake_path', + 'dirName': 'fake_dir', + } + mock_call.assert_called_once_with(url, data, self.post) + + def test__delete_filesystem_dir(self): + mock_call = self.mock_object(self.resthelper, + 'call') + self.mock_object(self.resthelper, + '_assert_result_code') + self.resthelper._delete_filesystem_dir('/fake_path/fake_dir') + url = 'rest/fileDir?path=/fake_path&dirName=fake_dir' + mock_call.assert_called_once_with(url, None, self.delete) + + @ddt.data('nfs', 'cifs') + def test__allow_access_rest(self, share_proto): + share_proto = share_proto.upper() + mock_anar = self.mock_object(self.resthelper, + '_allow_nfs_access_rest') + mock_acar = self.mock_object(self.resthelper, + '_allow_cifs_access_rest') + self.resthelper._allow_access_rest('fake_path', 'fake_access', + 'rw', share_proto) + if share_proto == 'NFS': + mock_anar.assert_called_once_with('fake_path', + 'fake_access', + 'rw') + elif share_proto == 'CIFS': + mock_acar.assert_called_once_with('fake_path', + 'fake_access', + 'rw') + + def test__allow_access_rest_proto_error(self): + self.assertRaises(exception.InvalidInput, + self.resthelper._allow_access_rest, + 'fake_path', + 'fake_access', + 'rw', + 'error_proto') + + def test__allow_nfs_access_rest(self): + mock_call = self.mock_object( + self.resthelper, + 'call', + mock.Mock(return_value=self.result_success)) + self.mock_object(self.resthelper, + '_assert_result_code') + self.resthelper._allow_nfs_access_rest('fake_path', '172.0.0.1', 'rw') + url = 'rest/nfsShareClient' + data = { + 'path': 'fake_path', + 'client': '172.0.0.1', + 'authority': 'rw', + } + mock_call.assert_called_once_with(url, data, self.post) + + @ddt.data( + {'access_to': 'fake_user', + 'group': False}, + {'access_to': 'fake_group', + 'group': True}, + {'access_to': '/fake_user', + 'group': False}, + {'access_to': '/fake_group', + 'group': True} + ) + @ddt.unpack + def test__allow_cifs_access_rest(self, access_to, group): + ug_type_list = { + 'localUser': '0', + 'localGroup': '1', + 'adUser': '2', + 'adGroup': '3', + } + if not group: + mock_call = self.mock_object( + self.resthelper, + 'call', + mock.Mock(return_value=self.result_success)) + else: + mock_call = self.mock_object( + self.resthelper, + 'call', + mock.Mock(side_effect=[self.result_failed_not_exist, + self.result_success])) + self.mock_object(self.resthelper, + '_assert_result_code') + self.resthelper._allow_cifs_access_rest('fake_path', + access_to, + 'rw') + url = 'rest/cifsShareClient' + actual_type = ug_type_list["localUser"] + if '/' not in access_to: + if not group: + actual_type = ug_type_list["localUser"] + access_to = access_to + else: + if not group: + actual_type = ug_type_list["adUser"] + access_to = access_to[access_to.index('/') + 1:] + data = { + 'path': 'fake_path', + 'right': 'rw', + 'ugName': access_to, + 'ugType': actual_type, + } + if not group: + mock_call.assert_called_once_with(url, data, self.post) + else: + mock_call.assert_called() + + def test__allow_cifs_access_rest_fail(self): + mock_call = self.mock_object( + self.resthelper, + 'call', + mock.Mock(side_effect=[self.result_failed_not_exist, + self.result_failed_not_exist])) + self.assertRaises(exception.InvalidShare, + self.resthelper._allow_cifs_access_rest, + 'fake_path', + 'fake_user', + 'rw') + mock_call.assert_called() + + def test__get_access_from_nfs_share(self): + fake_result = { + 'code': 0, + 'message': 'success', + 'data': { + "path": "fake_path", + "clientName": "fake_client", + "accessRight": "rw", + } + } + mock_call = self.mock_object(self.resthelper, + 'call', + mock.Mock(return_value=fake_result)) + self.mock_object(self.resthelper, + '_assert_result_code') + result = self.resthelper._get_access_from_nfs_share('fake_path', + 'fake_client') + expect = { + "path": "fake_path", + "clientName": "fake_client", + "accessRight": "rw", + } + self.assertEqual(expect, result) + url = 'rest/nfsShareClient?path=fake_path&client=fake_client' + mock_call.assert_called_once_with(url, None, self.get) + + @ddt.data({'access_to': 'fake_user', + 'ug_type': '0', + 'code': 0, + 'group': False}, + {'access_to': 'fake_user', + 'ug_type': None, + 'code': 0, + 'group': False}, + {'access_to': 'fake_group', + 'ug_type': None, + 'code': 0, + 'group': True}, + {'access_to': 'fake_user', + 'ug_type': None, + 'code': 4, + 'group': False}, + {'access_to': '/fake_user', + 'ug_type': None, + 'code': 0, + 'group': False}, + {'access_to': '/fake_group', + 'ug_type': None, + 'code': 0, + 'group': True}, + {'access_to': '/fake_user', + 'ug_type': None, + 'code': 4, + 'group': False}) + @ddt.unpack + def test__get_access_from_cifs_share(self, + access_to, ug_type, code, group): + fake_result_failed = { + 'code': code, + 'message': 'failed', + 'data': {} + } + fake_result = { + 'code': code, + 'message': 'success', + 'data': { + 'path': 'fake_path', + 'ugName': 'fake_user', + 'ugType': '0', + 'accessRight': 'rw' + } + } + fake_result_group = { + 'code': code, + 'message': 'success', + 'data': { + 'path': 'fake_path', + 'ugName': 'fake_group', + 'ugType': '1', + 'accessRight': 'rw' + } + } + if code == 4: + fake_result = fake_result_failed + ug_type_list = { + 'localUser': '0', + 'localGroup': '1', + 'adUser': '2', + 'adGroup': '3', + } + expect = { + 'path': 'fake_path', + 'ugName': 'fake_user', + 'ugType': '0', + 'accessRight': 'rw' + } + expect_group = { + 'path': 'fake_path', + 'ugName': 'fake_group', + 'ugType': '1', + 'accessRight': 'rw' + } + if '/' in access_to: + expect['ugType'] = '2' + expect_group['ugType'] = '3' + fake_result['data']['ugType'] = '2' + fake_result_group['data']['ugType'] = '3' + if ug_type is not None: + mock_call = self.mock_object(self.resthelper, + 'call', + mock.Mock(return_value=fake_result)) + else: + if not group: + mock_call = self.mock_object( + self.resthelper, + 'call', + mock.Mock(return_value=fake_result)) + else: + mock_call = self.mock_object( + self.resthelper, + 'call', + mock.Mock(side_effect=[fake_result_failed, + fake_result_group])) + + self.mock_object(self.resthelper, + '_assert_result_code') + result = self.resthelper._get_access_from_cifs_share('fake_path', + access_to, + ug_type) + if ug_type: + self.assertEqual(expect, result) + url = f'rest/cifsShareClient?path=fake_path&' \ + f'ugName={access_to}&ugType={ug_type}' + mock_call.assert_called_once_with(url, None, self.get) + else: + if '/' not in access_to: + if not group: + actual_type = ug_type_list["localUser"] + actual_access = access_to + else: + if not group: + actual_type = ug_type_list["adUser"] + actual_access = access_to[access_to.index('/') + 1:] + if code == 4: + self.assertIsNone(result) + else: + if not group: + self.assertEqual(expect, result) + url = f'rest/cifsShareClient?path=fake_path&' \ + f'ugName={actual_access}&' \ + f'ugType={actual_type}' + mock_call.assert_called_once_with(url, None, self.get) + else: + self.assertEqual(expect_group, result) + mock_call.assert_called() + + def test__get_all_nfs_access_rest(self): + fake_result = { + 'code': 0, + 'message': 'success', + 'data': [ + { + 'path': 'fake_path', + 'clientName': '172.0.0.1', + 'accessRight': 'rw' + }, + { + 'path': 'default_path', + 'clientName': '172.0.0.2', + 'accessRight': 'rw' + }] + } + mock_call = self.mock_object(self.resthelper, + 'call', + mock.Mock(return_value=fake_result)) + self.mock_object(self.resthelper, + '_assert_result_code') + result = self.resthelper._get_all_nfs_access_rest( + '/manila_fakeid/manila_fakeid') + expect = [ + { + 'share_path': 'fake_path', + 'access_to': '172.0.0.1', + 'access_level': 'rw' + }, + { + 'share_path': 'default_path', + 'access_to': '172.0.0.2', + 'access_level': 'rw' + }] + self.assertEqual(expect, result) + url = 'rest/allNfsShareClient?path=/manila_fakeid/manila_fakeid' + mock_call.assert_called_once_with(url, None, self.get) + + def test__get_all_cifs_access_rest(self): + fake_result = { + 'code': 0, + 'message': 'success', + 'data': [ + { + 'path': 'fake_path', + 'ugName': 'user_name', + 'ugType': '0', + 'accessRight': 'rw' + }, + { + 'path': 'default_path', + 'ugName': 'manilanobody', + 'ugType': '0', + 'accessRight': 'rw' + }] + } + mock_call = self.mock_object(self.resthelper, + 'call', + mock.Mock(return_value=fake_result)) + self.mock_object(self.resthelper, + '_assert_result_code') + result = self.resthelper._get_all_cifs_access_rest( + '/manila_fakeid/manila_fakeid') + expect = [ + { + 'share_path': 'fake_path', + 'access_to': 'user_name', + 'ugType': '0', + 'access_level': 'rw' + }, + { + 'share_path': 'default_path', + 'access_to': 'manilanobody', + 'ugType': '0', + 'access_level': 'rw' + }] + self.assertEqual(expect, result) + url = 'rest/allCifsShareClient?path=/manila_fakeid/manila_fakeid' + mock_call.assert_called_once_with(url, None, self.get) + + def test__change_nfs_access_rest(self): + mock_call = self.mock_object(self.resthelper, + 'call') + self.mock_object(self.resthelper, + '_assert_result_code') + self.resthelper._change_nfs_access_rest( + '/manila_fakeid/manila_fakeid', '172.0.0.1', 'rw') + url = 'rest/nfsShareClient' + data = { + 'path': '/manila_fakeid/manila_fakeid', + 'oldNfsClientName': '172.0.0.1', + 'clientName': '', + 'accessRight': 'rw', + 'allSquash': '', + 'rootSquash': '', + 'secure': '', + 'anonuid': '', + 'anongid': '', + } + mock_call.assert_called_once_with(url, data, self.put) + + def test__change_cifs_access_rest(self): + mock_call = self.mock_object(self.resthelper, + 'call') + self.mock_object(self.resthelper, + '_assert_result_code') + self.resthelper._change_cifs_access_rest( + '/manila_fakeid/manila_fakeid', '/fake_user', 'rw', '0') + url = 'rest/cifsShareClient' + data = { + 'path': '/manila_fakeid/manila_fakeid', + 'right': 'rw', + 'ugName': 'fake_user', + 'ugType': '0', + } + mock_call.assert_called_once_with(url, data, self.put) + + def test__delete_nfs_access_rest(self): + mock_call = self.mock_object(self.resthelper, + 'call') + self.mock_object(self.resthelper, + '_assert_result_code') + self.resthelper._delete_nfs_access_rest( + '/manila_fakeid/manila_fakeid', '*') + url = 'rest/nfsShareClient?path=/manila_fakeid/manila_fakeid&client=*' + mock_call.assert_called_once_with(url, None, self.delete) + + def test__delete_cifs_access_rest(self): + mock_call = self.mock_object(self.resthelper, + 'call') + self.mock_object(self.resthelper, + '_assert_result_code') + self.resthelper._delete_cifs_access_rest( + '/manila_fakeid/manila_fakeid', 'fake_user', '0') + url = 'rest/cifsShareClient?path=/manila_fakeid/manila_fakeid' \ + '&ugName=fake_user&ugType=0' + mock_call.assert_called_once_with(url, None, self.delete) + + def test__get_nfs_service_status(self): + fake_result = { + 'code': 0, + 'message': 'success', + 'data': { + 'serviceStatus': constants.NFS_ENABLED, + 'nfs3Status': constants.NFS_SUPPORTED, + 'nfs4Status': constants.NFS_SUPPORTED + } + } + mock_call = self.mock_object(self.resthelper, + 'call', + mock.Mock(return_value=fake_result)) + self.mock_object(self.resthelper, + '_assert_result_code') + result = self.resthelper._get_nfs_service_status() + expect = { + 'serviceStatus': constants.NFS_ENABLED, + 'nfs3Status': constants.NFS_SUPPORTED, + 'nfs4Status': constants.NFS_SUPPORTED + } + self.assertEqual(expect, result) + url = 'rest/nfsService' + mock_call.assert_called_once_with(url, None, self.get) + + def test__start_nfs_service(self): + mock_call = self.mock_object(self.resthelper, + 'call') + self.mock_object(self.resthelper, + '_assert_result_code') + self.resthelper._start_nfs_service() + url = 'rest/nfsService' + data = { + "openStatus": "1", + } + mock_call.assert_called_once_with(url, data, self.put) + + def test__config_nfs_service(self): + mock_call = self.mock_object(self.resthelper, + 'call') + self.mock_object(self.resthelper, + '_assert_result_code') + self.resthelper._config_nfs_service() + url = 'rest/nfsConfig' + data = { + 'configNfs3': "yes", + 'configNfs4': "yes", + } + mock_call.assert_called_once_with(url, data, self.put) + + def test__get_cifs_service_status(self): + mock_call = self.mock_object( + self.resthelper, + 'call', + mock.Mock(return_value=self.result_success_return_1)) + self.mock_object(self.resthelper, + '_assert_result_code') + result = self.resthelper._get_cifs_service_status() + self.assertEqual('1', result) + url = 'rest/cifsService' + mock_call.assert_called_once_with(url, None, self.get) + + def test__start_cifs_service(self): + mock_call = self.mock_object(self.resthelper, + 'call') + self.mock_object(self.resthelper, + '_assert_result_code') + self.resthelper._start_cifs_service() + url = 'rest/cifsService' + data = { + 'openStatus': '1', + } + mock_call.assert_called_once_with(url, data, self.put) + + def test__config_cifs_service(self): + mock_call = self.mock_object(self.resthelper, + 'call') + self.mock_object(self.resthelper, + '_assert_result_code') + self.resthelper._config_cifs_service() + url = 'rest/cifsConfig' + data = { + 'workName': 'manila', + 'description': '', + 'access_way': 'user', + 'isCache': 'no', + 'adsName': '', + 'adsIP': '', + 'adsUSER': '', + 'adsPASSWD': '', + 'allowList': [], + 'denyList': [], + } + mock_call.assert_called_once_with(url, data, self.put) + + def test__get_all_pool(self): + mock_call = self.mock_object( + self.resthelper, + 'call', + mock.Mock(return_value=self.result_success_storage_pools)) + self.mock_object(self.resthelper, + '_assert_result_code') + result = self.resthelper._get_all_pool() + self.assertEqual(self.result_success_storage_pools, result) + url = 'rest/storagepool' + mock_call.assert_called_once_with(url, None, self.get) + + def test__query_user(self): + mock_call = self.mock_object( + self.resthelper, + 'call', + mock.Mock(return_value=self.result_success_return_0)) + self.mock_object(self.resthelper, + '_assert_result_code') + result = self.resthelper._query_user('fake_user') + self.assertEqual('0', result) + url = 'rest/user/fake_user' + mock_call.assert_called_once_with(url, None, self.get) + + def test__add_localuser(self): + mock_call = self.mock_object(self.resthelper, + 'call') + self.mock_object(self.resthelper, + '_assert_result_code') + self.resthelper._add_localuser('fake_user', + 'fake_passwd', 'fake_group') + url = 'rest/localUser' + data = { + 'userName': 'fake_user', + 'mgGroup': 'fake_group', + 'userPasswd': 'fake_passwd', + 'unusedGroup': []} + mock_call.assert_called_once_with(url, data, self.post) + + def test__query_group(self): + mock_call = self.mock_object( + self.resthelper, + 'call', + mock.Mock(return_value=self.result_success_return_0)) + self.mock_object(self.resthelper, + '_assert_result_code') + result = self.resthelper._query_group('fake_group') + self.assertEqual('0', result) + url = 'rest/group/fake_group' + mock_call.assert_called_once_with(url, None, self.get) + + def test__add_localgroup(self): + mock_call = self.mock_object(self.resthelper, + 'call') + self.mock_object(self.resthelper, + '_assert_result_code') + self.resthelper._add_localgroup('fake_group') + url = 'rest/localGroup' + data = {'groupName': 'fake_group'} + mock_call.assert_called_once_with(url, data, self.post) diff --git a/releasenotes/notes/add-quotas-section-0e1e638a8f14d26e.yaml b/releasenotes/notes/add-quotas-section-0e1e638a8f14d26e.yaml index e69b07eff5..453c00308e 100644 --- a/releasenotes/notes/add-quotas-section-0e1e638a8f14d26e.yaml +++ b/releasenotes/notes/add-quotas-section-0e1e638a8f14d26e.yaml @@ -6,9 +6,9 @@ deprecations: the ``[DEFAULT]`` section. The existing options in the ``[DEFAULT]`` section have been deprecated and will be removed in a future release. Options that have changed in this releases are: - - - ``[quota]/shares`` - previously ``[DEFAULT]/shares_quota`` - - ``[quota]/snapshots`` - previously ``[DEFAULT]/snapshots_quota`` + + - ``[quota]/shares`` - previously ``[DEFAULT]/shares_quota`` + - ``[quota]/snapshots`` - previously ``[DEFAULT]/snapshots_quota`` - ``[quota]/gigabytes`` - previously ``[DEFAULT]/quota_gigabytes`` - ``[quota]/per_share_gigabytes`` - previously ``[DEFAULT]/quota_per_share_gigabytes`` - ``[quota]/snapshot_gigabytes`` - previously ``[DEFAULT]/quota_snapshot_gigabytes`` diff --git a/releasenotes/notes/bug-1721787-fix-getting-share-networks-and-security-services-error-7e5e7981fcbf2b53.yaml b/releasenotes/notes/bug-1721787-fix-getting-share-networks-and-security-services-error-7e5e7981fcbf2b53.yaml old mode 100755 new mode 100644 diff --git a/releasenotes/notes/emc-unity-manila-support-d4f5a410501cfdae.yaml b/releasenotes/notes/emc-unity-manila-support-d4f5a410501cfdae.yaml index d63ccd9e52..9a6bc0f54b 100644 --- a/releasenotes/notes/emc-unity-manila-support-d4f5a410501cfdae.yaml +++ b/releasenotes/notes/emc-unity-manila-support-d4f5a410501cfdae.yaml @@ -1,11 +1,11 @@ ---- -prelude: > - Add a new EMC Unity plugin in manila which allows user to create NFS/CIFS - share with an EMC Unity backend. -features: - - | - Add a new Unity plugin in manila which allows user to create NFS/CIFS - share with an EMC Unity backend. This plugin performs the operations on - Unity by REST API. -issues: - - EMC Unity does not support the same IP in different VLANs. +--- +prelude: > + Add a new EMC Unity plugin in manila which allows user to create NFS/CIFS + share with an EMC Unity backend. +features: + - | + Add a new Unity plugin in manila which allows user to create NFS/CIFS + share with an EMC Unity backend. This plugin performs the operations on + Unity by REST API. +issues: + - EMC Unity does not support the same IP in different VLANs. diff --git a/releasenotes/notes/huawei-pool-disktype-support-0a52ba5d44da55f9.yaml b/releasenotes/notes/huawei-pool-disktype-support-0a52ba5d44da55f9.yaml index 999c4549ee..42bf54b5a1 100644 --- a/releasenotes/notes/huawei-pool-disktype-support-0a52ba5d44da55f9.yaml +++ b/releasenotes/notes/huawei-pool-disktype-support-0a52ba5d44da55f9.yaml @@ -1,5 +1,5 @@ ---- -features: - - Add support for reporting pool disk type in Huawei driver. - `huawei_disk_type` extra-spec in the share type. Valid values - for this extra-spec are 'ssd', 'sas', 'nl_sas' or 'mix'. +--- +features: + - Add support for reporting pool disk type in Huawei driver. + `huawei_disk_type` extra-spec in the share type. Valid values + for this extra-spec are 'ssd', 'sas', 'nl_sas' or 'mix'. diff --git a/releasenotes/notes/netapp-ontap-rest-api-client-4c83c7b931f950cf.yaml b/releasenotes/notes/netapp-ontap-rest-api-client-4c83c7b931f950cf.yaml index 05a6eb9732..5ae11d162f 100644 --- a/releasenotes/notes/netapp-ontap-rest-api-client-4c83c7b931f950cf.yaml +++ b/releasenotes/notes/netapp-ontap-rest-api-client-4c83c7b931f950cf.yaml @@ -7,8 +7,8 @@ features: approach and new REST client. It is default to `True`, meaning that the drivers will keep working as before using ZAPI operations. If desired, this option can be set to `False` connecting with new REST client that performs - REST API operations if it is available, otherwise falls back to ZAPI. - + REST API operations if it is available, otherwise falls back to ZAPI. + Also, an option called `netapp_rest_operation_timeout` was added to allow the user to set the maximum amount of time expected to get an output from a synchronous operation when using REST API. By default, the timeout value diff --git a/releasenotes/notes/powermax-rebrand-manila-a46a0c2ac0aa77ed.yaml b/releasenotes/notes/powermax-rebrand-manila-a46a0c2ac0aa77ed.yaml index ba9c42c933..0f8d56dd7e 100644 --- a/releasenotes/notes/powermax-rebrand-manila-a46a0c2ac0aa77ed.yaml +++ b/releasenotes/notes/powermax-rebrand-manila-a46a0c2ac0aa77ed.yaml @@ -17,4 +17,4 @@ upgrade: ``emc_nas_server_container``, ``emc_nas_pool_names`` and ``emc_interface_ports`` can no longer be used. They must be replaced by ``powermax_server_container``, ``powermax_share_data_pools`` and - ``powermax_ethernet_ports`` respectively. + ``powermax_ethernet_ports`` respectively. diff --git a/releasenotes/notes/zfsonlinux-driver-improvement-create-share-from-snapshot-another-backend-44296f572681be35.yaml b/releasenotes/notes/zfsonlinux-driver-improvement-create-share-from-snapshot-another-backend-44296f572681be35.yaml index 42e04e8bc3..857a6b8174 100644 --- a/releasenotes/notes/zfsonlinux-driver-improvement-create-share-from-snapshot-another-backend-44296f572681be35.yaml +++ b/releasenotes/notes/zfsonlinux-driver-improvement-create-share-from-snapshot-another-backend-44296f572681be35.yaml @@ -4,4 +4,4 @@ upgrade: in the ZFSonLinux driver. Now, the operator using the ZFSonLinux driver can create a share from snapshot in different pools or backends by specifying the Manila API configuration option - [DEFAULT]/use_scheduler_creating_share_from_snapshot. + [DEFAULT]/use_scheduler_creating_share_from_snapshot. diff --git a/tools/test-setup.sh b/tools/test-setup.sh index b112326fbd..060eeaf901 100755 --- a/tools/test-setup.sh +++ b/tools/test-setup.sh @@ -39,7 +39,7 @@ DB_ROOT_PW=${POSTGRES_ROOT_PW:-insecure_slave} # Setup user root_roles=$(sudo -H -u postgres psql -t -c " - SELECT 'HERE' from pg_roles where rolname='$DB_USER'") + SELECT 'HERE' from pg_roles where rolname='$DB_USER'") if [[ ${root_roles} == *HERE ]];then sudo -H -u postgres psql -c "ALTER ROLE $DB_USER WITH SUPERUSER LOGIN PASSWORD '$DB_PW'" else