mirror of
				https://github.com/optim-enterprises-bv/kubernetes.git
				synced 2025-10-31 02:08:13 +00:00 
			
		
		
		
	Merge pull request #40423 from mkutsevol/feature/openstack_cinder_v1_2_auto
Automatic merge from submit-queue (batch tested with PRs 43681, 40423, 43562, 43008, 43381) Openstack cinder v1/v2/auto API support **What this PR does / why we need it**: It adds support for v2 cinder API + autodetection of available cinder API level (as in LBs). **Which issue this PR fixes** *(optional, in `fixes #<issue number>(, fixes #<issue_number>, ...)` format, will close that issue when PR gets merged)*: fixes #39572 **Special notes for your reviewer**: Based on work by @anguslees. The first two commits are just rebased from https://github.com/kubernetes/kubernetes/pull/36344 which already had a lgtm by @jbeda **Release note**: ``` Add support for v2 cinder API for openstack cloud provider. By default it autodetects the available version. ```
This commit is contained in:
		
							
								
								
									
										10
									
								
								Godeps/Godeps.json
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										10
									
								
								Godeps/Godeps.json
									
									
									
										generated
									
									
									
								
							| @@ -1,7 +1,7 @@ | |||||||
| { | { | ||||||
| 	"ImportPath": "k8s.io/kubernetes", | 	"ImportPath": "k8s.io/kubernetes", | ||||||
| 	"GoVersion": "go1.7", | 	"GoVersion": "go1.7", | ||||||
| 	"GodepVersion": "v79", | 	"GodepVersion": "v74", | ||||||
| 	"Packages": [ | 	"Packages": [ | ||||||
| 		"github.com/ugorji/go/codec/codecgen", | 		"github.com/ugorji/go/codec/codecgen", | ||||||
| 		"github.com/onsi/ginkgo/ginkgo", | 		"github.com/onsi/ginkgo/ginkgo", | ||||||
| @@ -1416,10 +1416,18 @@ | |||||||
| 			"ImportPath": "github.com/gophercloud/gophercloud/openstack", | 			"ImportPath": "github.com/gophercloud/gophercloud/openstack", | ||||||
| 			"Rev": "12f19e5e04d617182cffa5c11f189ef0013b9791" | 			"Rev": "12f19e5e04d617182cffa5c11f189ef0013b9791" | ||||||
| 		}, | 		}, | ||||||
|  | 		{ | ||||||
|  | 			"ImportPath": "github.com/gophercloud/gophercloud/openstack/blockstorage/v1/apiversions", | ||||||
|  | 			"Rev": "12f19e5e04d617182cffa5c11f189ef0013b9791" | ||||||
|  | 		}, | ||||||
| 		{ | 		{ | ||||||
| 			"ImportPath": "github.com/gophercloud/gophercloud/openstack/blockstorage/v1/volumes", | 			"ImportPath": "github.com/gophercloud/gophercloud/openstack/blockstorage/v1/volumes", | ||||||
| 			"Rev": "12f19e5e04d617182cffa5c11f189ef0013b9791" | 			"Rev": "12f19e5e04d617182cffa5c11f189ef0013b9791" | ||||||
| 		}, | 		}, | ||||||
|  | 		{ | ||||||
|  | 			"ImportPath": "github.com/gophercloud/gophercloud/openstack/blockstorage/v2/volumes", | ||||||
|  | 			"Rev": "12f19e5e04d617182cffa5c11f189ef0013b9791" | ||||||
|  | 		}, | ||||||
| 		{ | 		{ | ||||||
| 			"ImportPath": "github.com/gophercloud/gophercloud/openstack/common/extensions", | 			"ImportPath": "github.com/gophercloud/gophercloud/openstack/common/extensions", | ||||||
| 			"Rev": "12f19e5e04d617182cffa5c11f189ef0013b9791" | 			"Rev": "12f19e5e04d617182cffa5c11f189ef0013b9791" | ||||||
|   | |||||||
							
								
								
									
										398
									
								
								Godeps/LICENSES
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										398
									
								
								Godeps/LICENSES
									
									
									
										generated
									
									
									
								
							| @@ -47689,6 +47689,205 @@ specific language governing permissions and limitations under the License. | |||||||
| ================================================================================ | ================================================================================ | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
|  | ================================================================================ | ||||||
|  | = vendor/github.com/gophercloud/gophercloud/openstack/blockstorage/v1/apiversions licensed under: = | ||||||
|  | 
 | ||||||
|  | Copyright 2012-2013 Rackspace, Inc. | ||||||
|  | 
 | ||||||
|  | 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.                                 | ||||||
|  | 
 | ||||||
|  | ------ | ||||||
|  |   | ||||||
|  | 				Apache License | ||||||
|  |                            Version 2.0, January 2004 | ||||||
|  |                         http://www.apache.org/licenses/ | ||||||
|  | 
 | ||||||
|  |    TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION | ||||||
|  | 
 | ||||||
|  |    1. Definitions. | ||||||
|  | 
 | ||||||
|  |       "License" shall mean the terms and conditions for use, reproduction, | ||||||
|  |       and distribution as defined by Sections 1 through 9 of this document. | ||||||
|  | 
 | ||||||
|  |       "Licensor" shall mean the copyright owner or entity authorized by | ||||||
|  |       the copyright owner that is granting the License. | ||||||
|  | 
 | ||||||
|  |       "Legal Entity" shall mean the union of the acting entity and all | ||||||
|  |       other entities that control, are controlled by, or are under common | ||||||
|  |       control with that entity. For the purposes of this definition, | ||||||
|  |       "control" means (i) the power, direct or indirect, to cause the | ||||||
|  |       direction or management of such entity, whether by contract or | ||||||
|  |       otherwise, or (ii) ownership of fifty percent (50%) or more of the | ||||||
|  |       outstanding shares, or (iii) beneficial ownership of such entity. | ||||||
|  | 
 | ||||||
|  |       "You" (or "Your") shall mean an individual or Legal Entity | ||||||
|  |       exercising permissions granted by this License. | ||||||
|  | 
 | ||||||
|  |       "Source" form shall mean the preferred form for making modifications, | ||||||
|  |       including but not limited to software source code, documentation | ||||||
|  |       source, and configuration files. | ||||||
|  | 
 | ||||||
|  |       "Object" form shall mean any form resulting from mechanical | ||||||
|  |       transformation or translation of a Source form, including but | ||||||
|  |       not limited to compiled object code, generated documentation, | ||||||
|  |       and conversions to other media types. | ||||||
|  | 
 | ||||||
|  |       "Work" shall mean the work of authorship, whether in Source or | ||||||
|  |       Object form, made available under the License, as indicated by a | ||||||
|  |       copyright notice that is included in or attached to the work | ||||||
|  |       (an example is provided in the Appendix below). | ||||||
|  | 
 | ||||||
|  |       "Derivative Works" shall mean any work, whether in Source or Object | ||||||
|  |       form, that is based on (or derived from) the Work and for which the | ||||||
|  |       editorial revisions, annotations, elaborations, or other modifications | ||||||
|  |       represent, as a whole, an original work of authorship. For the purposes | ||||||
|  |       of this License, Derivative Works shall not include works that remain | ||||||
|  |       separable from, or merely link (or bind by name) to the interfaces of, | ||||||
|  |       the Work and Derivative Works thereof. | ||||||
|  | 
 | ||||||
|  |       "Contribution" shall mean any work of authorship, including | ||||||
|  |       the original version of the Work and any modifications or additions | ||||||
|  |       to that Work or Derivative Works thereof, that is intentionally | ||||||
|  |       submitted to Licensor for inclusion in the Work by the copyright owner | ||||||
|  |       or by an individual or Legal Entity authorized to submit on behalf of | ||||||
|  |       the copyright owner. For the purposes of this definition, "submitted" | ||||||
|  |       means any form of electronic, verbal, or written communication sent | ||||||
|  |       to the Licensor or its representatives, including but not limited to | ||||||
|  |       communication on electronic mailing lists, source code control systems, | ||||||
|  |       and issue tracking systems that are managed by, or on behalf of, the | ||||||
|  |       Licensor for the purpose of discussing and improving the Work, but | ||||||
|  |       excluding communication that is conspicuously marked or otherwise | ||||||
|  |       designated in writing by the copyright owner as "Not a Contribution." | ||||||
|  | 
 | ||||||
|  |       "Contributor" shall mean Licensor and any individual or Legal Entity | ||||||
|  |       on behalf of whom a Contribution has been received by Licensor and | ||||||
|  |       subsequently incorporated within the Work. | ||||||
|  | 
 | ||||||
|  |    2. Grant of Copyright License. Subject to the terms and conditions of | ||||||
|  |       this License, each Contributor hereby grants to You a perpetual, | ||||||
|  |       worldwide, non-exclusive, no-charge, royalty-free, irrevocable | ||||||
|  |       copyright license to reproduce, prepare Derivative Works of, | ||||||
|  |       publicly display, publicly perform, sublicense, and distribute the | ||||||
|  |       Work and such Derivative Works in Source or Object form. | ||||||
|  | 
 | ||||||
|  |    3. Grant of Patent License. Subject to the terms and conditions of | ||||||
|  |       this License, each Contributor hereby grants to You a perpetual, | ||||||
|  |       worldwide, non-exclusive, no-charge, royalty-free, irrevocable | ||||||
|  |       (except as stated in this section) patent license to make, have made, | ||||||
|  |       use, offer to sell, sell, import, and otherwise transfer the Work, | ||||||
|  |       where such license applies only to those patent claims licensable | ||||||
|  |       by such Contributor that are necessarily infringed by their | ||||||
|  |       Contribution(s) alone or by combination of their Contribution(s) | ||||||
|  |       with the Work to which such Contribution(s) was submitted. If You | ||||||
|  |       institute patent litigation against any entity (including a | ||||||
|  |       cross-claim or counterclaim in a lawsuit) alleging that the Work | ||||||
|  |       or a Contribution incorporated within the Work constitutes direct | ||||||
|  |       or contributory patent infringement, then any patent licenses | ||||||
|  |       granted to You under this License for that Work shall terminate | ||||||
|  |       as of the date such litigation is filed. | ||||||
|  | 
 | ||||||
|  |    4. Redistribution. You may reproduce and distribute copies of the | ||||||
|  |       Work or Derivative Works thereof in any medium, with or without | ||||||
|  |       modifications, and in Source or Object form, provided that You | ||||||
|  |       meet the following conditions: | ||||||
|  | 
 | ||||||
|  |       (a) You must give any other recipients of the Work or | ||||||
|  |           Derivative Works a copy of this License; and | ||||||
|  | 
 | ||||||
|  |       (b) You must cause any modified files to carry prominent notices | ||||||
|  |           stating that You changed the files; and | ||||||
|  | 
 | ||||||
|  |       (c) You must retain, in the Source form of any Derivative Works | ||||||
|  |           that You distribute, all copyright, patent, trademark, and | ||||||
|  |           attribution notices from the Source form of the Work, | ||||||
|  |           excluding those notices that do not pertain to any part of | ||||||
|  |           the Derivative Works; and | ||||||
|  | 
 | ||||||
|  |       (d) If the Work includes a "NOTICE" text file as part of its | ||||||
|  |           distribution, then any Derivative Works that You distribute must | ||||||
|  |           include a readable copy of the attribution notices contained | ||||||
|  |           within such NOTICE file, excluding those notices that do not | ||||||
|  |           pertain to any part of the Derivative Works, in at least one | ||||||
|  |           of the following places: within a NOTICE text file distributed | ||||||
|  |           as part of the Derivative Works; within the Source form or | ||||||
|  |           documentation, if provided along with the Derivative Works; or, | ||||||
|  |           within a display generated by the Derivative Works, if and | ||||||
|  |           wherever such third-party notices normally appear. The contents | ||||||
|  |           of the NOTICE file are for informational purposes only and | ||||||
|  |           do not modify the License. You may add Your own attribution | ||||||
|  |           notices within Derivative Works that You distribute, alongside | ||||||
|  |           or as an addendum to the NOTICE text from the Work, provided | ||||||
|  |           that such additional attribution notices cannot be construed | ||||||
|  |           as modifying the License. | ||||||
|  | 
 | ||||||
|  |       You may add Your own copyright statement to Your modifications and | ||||||
|  |       may provide additional or different license terms and conditions | ||||||
|  |       for use, reproduction, or distribution of Your modifications, or | ||||||
|  |       for any such Derivative Works as a whole, provided Your use, | ||||||
|  |       reproduction, and distribution of the Work otherwise complies with | ||||||
|  |       the conditions stated in this License. | ||||||
|  | 
 | ||||||
|  |    5. Submission of Contributions. Unless You explicitly state otherwise, | ||||||
|  |       any Contribution intentionally submitted for inclusion in the Work | ||||||
|  |       by You to the Licensor shall be under the terms and conditions of | ||||||
|  |       this License, without any additional terms or conditions. | ||||||
|  |       Notwithstanding the above, nothing herein shall supersede or modify | ||||||
|  |       the terms of any separate license agreement you may have executed | ||||||
|  |       with Licensor regarding such Contributions. | ||||||
|  | 
 | ||||||
|  |    6. Trademarks. This License does not grant permission to use the trade | ||||||
|  |       names, trademarks, service marks, or product names of the Licensor, | ||||||
|  |       except as required for reasonable and customary use in describing the | ||||||
|  |       origin of the Work and reproducing the content of the NOTICE file. | ||||||
|  | 
 | ||||||
|  |    7. Disclaimer of Warranty. Unless required by applicable law or | ||||||
|  |       agreed to in writing, Licensor provides the Work (and each | ||||||
|  |       Contributor provides its Contributions) on an "AS IS" BASIS, | ||||||
|  |       WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or | ||||||
|  |       implied, including, without limitation, any warranties or conditions | ||||||
|  |       of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A | ||||||
|  |       PARTICULAR PURPOSE. You are solely responsible for determining the | ||||||
|  |       appropriateness of using or redistributing the Work and assume any | ||||||
|  |       risks associated with Your exercise of permissions under this License. | ||||||
|  | 
 | ||||||
|  |    8. Limitation of Liability. In no event and under no legal theory, | ||||||
|  |       whether in tort (including negligence), contract, or otherwise, | ||||||
|  |       unless required by applicable law (such as deliberate and grossly | ||||||
|  |       negligent acts) or agreed to in writing, shall any Contributor be | ||||||
|  |       liable to You for damages, including any direct, indirect, special, | ||||||
|  |       incidental, or consequential damages of any character arising as a | ||||||
|  |       result of this License or out of the use or inability to use the | ||||||
|  |       Work (including but not limited to damages for loss of goodwill, | ||||||
|  |       work stoppage, computer failure or malfunction, or any and all | ||||||
|  |       other commercial damages or losses), even if such Contributor | ||||||
|  |       has been advised of the possibility of such damages. | ||||||
|  | 
 | ||||||
|  |    9. Accepting Warranty or Additional Liability. While redistributing | ||||||
|  |       the Work or Derivative Works thereof, You may choose to offer, | ||||||
|  |       and charge a fee for, acceptance of support, warranty, indemnity, | ||||||
|  |       or other liability obligations and/or rights consistent with this | ||||||
|  |       License. However, in accepting such obligations, You may act only | ||||||
|  |       on Your own behalf and on Your sole responsibility, not on behalf | ||||||
|  |       of any other Contributor, and only if You agree to indemnify, | ||||||
|  |       defend, and hold each Contributor harmless for any liability | ||||||
|  |       incurred by, or claims asserted against, such Contributor by reason | ||||||
|  |       of your accepting any such warranty or additional liability. | ||||||
|  | 
 | ||||||
|  |    END OF TERMS AND CONDITIONS | ||||||
|  | 
 | ||||||
|  | = vendor/github.com/gophercloud/gophercloud/LICENSE dd19699707373c2ca31531a659130416  - | ||||||
|  | ================================================================================ | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
| ================================================================================ | ================================================================================ | ||||||
| = vendor/github.com/gophercloud/gophercloud/openstack/blockstorage/v1/volumes licensed under: = | = vendor/github.com/gophercloud/gophercloud/openstack/blockstorage/v1/volumes licensed under: = | ||||||
| 
 | 
 | ||||||
| @@ -47888,6 +48087,205 @@ specific language governing permissions and limitations under the License. | |||||||
| ================================================================================ | ================================================================================ | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
|  | ================================================================================ | ||||||
|  | = vendor/github.com/gophercloud/gophercloud/openstack/blockstorage/v2/volumes licensed under: = | ||||||
|  | 
 | ||||||
|  | Copyright 2012-2013 Rackspace, Inc. | ||||||
|  | 
 | ||||||
|  | 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.                                 | ||||||
|  | 
 | ||||||
|  | ------ | ||||||
|  |   | ||||||
|  | 				Apache License | ||||||
|  |                            Version 2.0, January 2004 | ||||||
|  |                         http://www.apache.org/licenses/ | ||||||
|  | 
 | ||||||
|  |    TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION | ||||||
|  | 
 | ||||||
|  |    1. Definitions. | ||||||
|  | 
 | ||||||
|  |       "License" shall mean the terms and conditions for use, reproduction, | ||||||
|  |       and distribution as defined by Sections 1 through 9 of this document. | ||||||
|  | 
 | ||||||
|  |       "Licensor" shall mean the copyright owner or entity authorized by | ||||||
|  |       the copyright owner that is granting the License. | ||||||
|  | 
 | ||||||
|  |       "Legal Entity" shall mean the union of the acting entity and all | ||||||
|  |       other entities that control, are controlled by, or are under common | ||||||
|  |       control with that entity. For the purposes of this definition, | ||||||
|  |       "control" means (i) the power, direct or indirect, to cause the | ||||||
|  |       direction or management of such entity, whether by contract or | ||||||
|  |       otherwise, or (ii) ownership of fifty percent (50%) or more of the | ||||||
|  |       outstanding shares, or (iii) beneficial ownership of such entity. | ||||||
|  | 
 | ||||||
|  |       "You" (or "Your") shall mean an individual or Legal Entity | ||||||
|  |       exercising permissions granted by this License. | ||||||
|  | 
 | ||||||
|  |       "Source" form shall mean the preferred form for making modifications, | ||||||
|  |       including but not limited to software source code, documentation | ||||||
|  |       source, and configuration files. | ||||||
|  | 
 | ||||||
|  |       "Object" form shall mean any form resulting from mechanical | ||||||
|  |       transformation or translation of a Source form, including but | ||||||
|  |       not limited to compiled object code, generated documentation, | ||||||
|  |       and conversions to other media types. | ||||||
|  | 
 | ||||||
|  |       "Work" shall mean the work of authorship, whether in Source or | ||||||
|  |       Object form, made available under the License, as indicated by a | ||||||
|  |       copyright notice that is included in or attached to the work | ||||||
|  |       (an example is provided in the Appendix below). | ||||||
|  | 
 | ||||||
|  |       "Derivative Works" shall mean any work, whether in Source or Object | ||||||
|  |       form, that is based on (or derived from) the Work and for which the | ||||||
|  |       editorial revisions, annotations, elaborations, or other modifications | ||||||
|  |       represent, as a whole, an original work of authorship. For the purposes | ||||||
|  |       of this License, Derivative Works shall not include works that remain | ||||||
|  |       separable from, or merely link (or bind by name) to the interfaces of, | ||||||
|  |       the Work and Derivative Works thereof. | ||||||
|  | 
 | ||||||
|  |       "Contribution" shall mean any work of authorship, including | ||||||
|  |       the original version of the Work and any modifications or additions | ||||||
|  |       to that Work or Derivative Works thereof, that is intentionally | ||||||
|  |       submitted to Licensor for inclusion in the Work by the copyright owner | ||||||
|  |       or by an individual or Legal Entity authorized to submit on behalf of | ||||||
|  |       the copyright owner. For the purposes of this definition, "submitted" | ||||||
|  |       means any form of electronic, verbal, or written communication sent | ||||||
|  |       to the Licensor or its representatives, including but not limited to | ||||||
|  |       communication on electronic mailing lists, source code control systems, | ||||||
|  |       and issue tracking systems that are managed by, or on behalf of, the | ||||||
|  |       Licensor for the purpose of discussing and improving the Work, but | ||||||
|  |       excluding communication that is conspicuously marked or otherwise | ||||||
|  |       designated in writing by the copyright owner as "Not a Contribution." | ||||||
|  | 
 | ||||||
|  |       "Contributor" shall mean Licensor and any individual or Legal Entity | ||||||
|  |       on behalf of whom a Contribution has been received by Licensor and | ||||||
|  |       subsequently incorporated within the Work. | ||||||
|  | 
 | ||||||
|  |    2. Grant of Copyright License. Subject to the terms and conditions of | ||||||
|  |       this License, each Contributor hereby grants to You a perpetual, | ||||||
|  |       worldwide, non-exclusive, no-charge, royalty-free, irrevocable | ||||||
|  |       copyright license to reproduce, prepare Derivative Works of, | ||||||
|  |       publicly display, publicly perform, sublicense, and distribute the | ||||||
|  |       Work and such Derivative Works in Source or Object form. | ||||||
|  | 
 | ||||||
|  |    3. Grant of Patent License. Subject to the terms and conditions of | ||||||
|  |       this License, each Contributor hereby grants to You a perpetual, | ||||||
|  |       worldwide, non-exclusive, no-charge, royalty-free, irrevocable | ||||||
|  |       (except as stated in this section) patent license to make, have made, | ||||||
|  |       use, offer to sell, sell, import, and otherwise transfer the Work, | ||||||
|  |       where such license applies only to those patent claims licensable | ||||||
|  |       by such Contributor that are necessarily infringed by their | ||||||
|  |       Contribution(s) alone or by combination of their Contribution(s) | ||||||
|  |       with the Work to which such Contribution(s) was submitted. If You | ||||||
|  |       institute patent litigation against any entity (including a | ||||||
|  |       cross-claim or counterclaim in a lawsuit) alleging that the Work | ||||||
|  |       or a Contribution incorporated within the Work constitutes direct | ||||||
|  |       or contributory patent infringement, then any patent licenses | ||||||
|  |       granted to You under this License for that Work shall terminate | ||||||
|  |       as of the date such litigation is filed. | ||||||
|  | 
 | ||||||
|  |    4. Redistribution. You may reproduce and distribute copies of the | ||||||
|  |       Work or Derivative Works thereof in any medium, with or without | ||||||
|  |       modifications, and in Source or Object form, provided that You | ||||||
|  |       meet the following conditions: | ||||||
|  | 
 | ||||||
|  |       (a) You must give any other recipients of the Work or | ||||||
|  |           Derivative Works a copy of this License; and | ||||||
|  | 
 | ||||||
|  |       (b) You must cause any modified files to carry prominent notices | ||||||
|  |           stating that You changed the files; and | ||||||
|  | 
 | ||||||
|  |       (c) You must retain, in the Source form of any Derivative Works | ||||||
|  |           that You distribute, all copyright, patent, trademark, and | ||||||
|  |           attribution notices from the Source form of the Work, | ||||||
|  |           excluding those notices that do not pertain to any part of | ||||||
|  |           the Derivative Works; and | ||||||
|  | 
 | ||||||
|  |       (d) If the Work includes a "NOTICE" text file as part of its | ||||||
|  |           distribution, then any Derivative Works that You distribute must | ||||||
|  |           include a readable copy of the attribution notices contained | ||||||
|  |           within such NOTICE file, excluding those notices that do not | ||||||
|  |           pertain to any part of the Derivative Works, in at least one | ||||||
|  |           of the following places: within a NOTICE text file distributed | ||||||
|  |           as part of the Derivative Works; within the Source form or | ||||||
|  |           documentation, if provided along with the Derivative Works; or, | ||||||
|  |           within a display generated by the Derivative Works, if and | ||||||
|  |           wherever such third-party notices normally appear. The contents | ||||||
|  |           of the NOTICE file are for informational purposes only and | ||||||
|  |           do not modify the License. You may add Your own attribution | ||||||
|  |           notices within Derivative Works that You distribute, alongside | ||||||
|  |           or as an addendum to the NOTICE text from the Work, provided | ||||||
|  |           that such additional attribution notices cannot be construed | ||||||
|  |           as modifying the License. | ||||||
|  | 
 | ||||||
|  |       You may add Your own copyright statement to Your modifications and | ||||||
|  |       may provide additional or different license terms and conditions | ||||||
|  |       for use, reproduction, or distribution of Your modifications, or | ||||||
|  |       for any such Derivative Works as a whole, provided Your use, | ||||||
|  |       reproduction, and distribution of the Work otherwise complies with | ||||||
|  |       the conditions stated in this License. | ||||||
|  | 
 | ||||||
|  |    5. Submission of Contributions. Unless You explicitly state otherwise, | ||||||
|  |       any Contribution intentionally submitted for inclusion in the Work | ||||||
|  |       by You to the Licensor shall be under the terms and conditions of | ||||||
|  |       this License, without any additional terms or conditions. | ||||||
|  |       Notwithstanding the above, nothing herein shall supersede or modify | ||||||
|  |       the terms of any separate license agreement you may have executed | ||||||
|  |       with Licensor regarding such Contributions. | ||||||
|  | 
 | ||||||
|  |    6. Trademarks. This License does not grant permission to use the trade | ||||||
|  |       names, trademarks, service marks, or product names of the Licensor, | ||||||
|  |       except as required for reasonable and customary use in describing the | ||||||
|  |       origin of the Work and reproducing the content of the NOTICE file. | ||||||
|  | 
 | ||||||
|  |    7. Disclaimer of Warranty. Unless required by applicable law or | ||||||
|  |       agreed to in writing, Licensor provides the Work (and each | ||||||
|  |       Contributor provides its Contributions) on an "AS IS" BASIS, | ||||||
|  |       WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or | ||||||
|  |       implied, including, without limitation, any warranties or conditions | ||||||
|  |       of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A | ||||||
|  |       PARTICULAR PURPOSE. You are solely responsible for determining the | ||||||
|  |       appropriateness of using or redistributing the Work and assume any | ||||||
|  |       risks associated with Your exercise of permissions under this License. | ||||||
|  | 
 | ||||||
|  |    8. Limitation of Liability. In no event and under no legal theory, | ||||||
|  |       whether in tort (including negligence), contract, or otherwise, | ||||||
|  |       unless required by applicable law (such as deliberate and grossly | ||||||
|  |       negligent acts) or agreed to in writing, shall any Contributor be | ||||||
|  |       liable to You for damages, including any direct, indirect, special, | ||||||
|  |       incidental, or consequential damages of any character arising as a | ||||||
|  |       result of this License or out of the use or inability to use the | ||||||
|  |       Work (including but not limited to damages for loss of goodwill, | ||||||
|  |       work stoppage, computer failure or malfunction, or any and all | ||||||
|  |       other commercial damages or losses), even if such Contributor | ||||||
|  |       has been advised of the possibility of such damages. | ||||||
|  | 
 | ||||||
|  |    9. Accepting Warranty or Additional Liability. While redistributing | ||||||
|  |       the Work or Derivative Works thereof, You may choose to offer, | ||||||
|  |       and charge a fee for, acceptance of support, warranty, indemnity, | ||||||
|  |       or other liability obligations and/or rights consistent with this | ||||||
|  |       License. However, in accepting such obligations, You may act only | ||||||
|  |       on Your own behalf and on Your sole responsibility, not on behalf | ||||||
|  |       of any other Contributor, and only if You agree to indemnify, | ||||||
|  |       defend, and hold each Contributor harmless for any liability | ||||||
|  |       incurred by, or claims asserted against, such Contributor by reason | ||||||
|  |       of your accepting any such warranty or additional liability. | ||||||
|  | 
 | ||||||
|  |    END OF TERMS AND CONDITIONS | ||||||
|  | 
 | ||||||
|  | = vendor/github.com/gophercloud/gophercloud/LICENSE dd19699707373c2ca31531a659130416  - | ||||||
|  | ================================================================================ | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
| ================================================================================ | ================================================================================ | ||||||
| = vendor/github.com/gophercloud/gophercloud/openstack/common/extensions licensed under: = | = vendor/github.com/gophercloud/gophercloud/openstack/common/extensions licensed under: = | ||||||
| 
 | 
 | ||||||
|   | |||||||
| @@ -29,7 +29,9 @@ go_library( | |||||||
|         "//vendor:github.com/golang/glog", |         "//vendor:github.com/golang/glog", | ||||||
|         "//vendor:github.com/gophercloud/gophercloud", |         "//vendor:github.com/gophercloud/gophercloud", | ||||||
|         "//vendor:github.com/gophercloud/gophercloud/openstack", |         "//vendor:github.com/gophercloud/gophercloud/openstack", | ||||||
|  |         "//vendor:github.com/gophercloud/gophercloud/openstack/blockstorage/v1/apiversions", | ||||||
|         "//vendor:github.com/gophercloud/gophercloud/openstack/blockstorage/v1/volumes", |         "//vendor:github.com/gophercloud/gophercloud/openstack/blockstorage/v1/volumes", | ||||||
|  |         "//vendor:github.com/gophercloud/gophercloud/openstack/blockstorage/v2/volumes", | ||||||
|         "//vendor:github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/volumeattach", |         "//vendor:github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/volumeattach", | ||||||
|         "//vendor:github.com/gophercloud/gophercloud/openstack/compute/v2/flavors", |         "//vendor:github.com/gophercloud/gophercloud/openstack/compute/v2/flavors", | ||||||
|         "//vendor:github.com/gophercloud/gophercloud/openstack/compute/v2/servers", |         "//vendor:github.com/gophercloud/gophercloud/openstack/compute/v2/servers", | ||||||
| @@ -72,6 +74,7 @@ go_test( | |||||||
|         "//pkg/api/v1:go_default_library", |         "//pkg/api/v1:go_default_library", | ||||||
|         "//pkg/cloudprovider:go_default_library", |         "//pkg/cloudprovider:go_default_library", | ||||||
|         "//vendor:github.com/gophercloud/gophercloud", |         "//vendor:github.com/gophercloud/gophercloud", | ||||||
|  |         "//vendor:github.com/gophercloud/gophercloud/openstack/blockstorage/v1/apiversions", | ||||||
|         "//vendor:github.com/gophercloud/gophercloud/openstack/compute/v2/servers", |         "//vendor:github.com/gophercloud/gophercloud/openstack/compute/v2/servers", | ||||||
|         "//vendor:k8s.io/apimachinery/pkg/apis/meta/v1", |         "//vendor:k8s.io/apimachinery/pkg/apis/meta/v1", | ||||||
|         "//vendor:k8s.io/apimachinery/pkg/types", |         "//vendor:k8s.io/apimachinery/pkg/types", | ||||||
|   | |||||||
| @@ -24,11 +24,13 @@ import ( | |||||||
| 	"io/ioutil" | 	"io/ioutil" | ||||||
| 	"net/http" | 	"net/http" | ||||||
| 	"regexp" | 	"regexp" | ||||||
|  | 	"sort" | ||||||
| 	"strings" | 	"strings" | ||||||
| 	"time" | 	"time" | ||||||
|  |  | ||||||
| 	"github.com/gophercloud/gophercloud" | 	"github.com/gophercloud/gophercloud" | ||||||
| 	"github.com/gophercloud/gophercloud/openstack" | 	"github.com/gophercloud/gophercloud/openstack" | ||||||
|  | 	apiversions_v1 "github.com/gophercloud/gophercloud/openstack/blockstorage/v1/apiversions" | ||||||
| 	"github.com/gophercloud/gophercloud/openstack/compute/v2/servers" | 	"github.com/gophercloud/gophercloud/openstack/compute/v2/servers" | ||||||
| 	"github.com/gophercloud/gophercloud/openstack/identity/v3/extensions/trusts" | 	"github.com/gophercloud/gophercloud/openstack/identity/v3/extensions/trusts" | ||||||
| 	tokens3 "github.com/gophercloud/gophercloud/openstack/identity/v3/tokens" | 	tokens3 "github.com/gophercloud/gophercloud/openstack/identity/v3/tokens" | ||||||
| @@ -89,7 +91,8 @@ type LoadBalancerOpts struct { | |||||||
| } | } | ||||||
|  |  | ||||||
| type BlockStorageOpts struct { | type BlockStorageOpts struct { | ||||||
| 	TrustDevicePath bool `gcfg:"trust-device-path"` // See Issue #33128 | 	BSVersion       string `gcfg:"bs-version"`        // overrides autodetection. v1 or v2. Defaults to auto | ||||||
|  | 	TrustDevicePath bool   `gcfg:"trust-device-path"` // See Issue #33128 | ||||||
| } | } | ||||||
|  |  | ||||||
| type RouterOpts struct { | type RouterOpts struct { | ||||||
| @@ -173,6 +176,7 @@ func readConfig(config io.Reader) (Config, error) { | |||||||
| 	var cfg Config | 	var cfg Config | ||||||
|  |  | ||||||
| 	// Set default values for config params | 	// Set default values for config params | ||||||
|  | 	cfg.BlockStorage.BSVersion = "auto" | ||||||
| 	cfg.BlockStorage.TrustDevicePath = false | 	cfg.BlockStorage.TrustDevicePath = false | ||||||
|  |  | ||||||
| 	err := gcfg.ReadInto(&cfg, config) | 	err := gcfg.ReadInto(&cfg, config) | ||||||
| @@ -535,3 +539,111 @@ func (os *OpenStack) Routes() (cloudprovider.Routes, bool) { | |||||||
|  |  | ||||||
| 	return r, true | 	return r, true | ||||||
| } | } | ||||||
|  |  | ||||||
|  | // Implementation of sort interface for blockstorage version probing | ||||||
|  | type APIVersionsByID []apiversions_v1.APIVersion | ||||||
|  |  | ||||||
|  | func (apiVersions APIVersionsByID) Len() int { | ||||||
|  | 	return len(apiVersions) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (apiVersions APIVersionsByID) Swap(i, j int) { | ||||||
|  | 	apiVersions[i], apiVersions[j] = apiVersions[j], apiVersions[i] | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (apiVersions APIVersionsByID) Less(i, j int) bool { | ||||||
|  | 	return apiVersions[i].ID > apiVersions[j].ID | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func autoVersionSelector(apiVersion *apiversions_v1.APIVersion) string { | ||||||
|  | 	switch strings.ToLower(apiVersion.ID) { | ||||||
|  | 	case "v2.0": | ||||||
|  | 		return "v2" | ||||||
|  | 	case "v1.0": | ||||||
|  | 		return "v1" | ||||||
|  | 	default: | ||||||
|  | 		return "" | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func doBsApiVersionAutodetect(availableApiVersions []apiversions_v1.APIVersion) string { | ||||||
|  | 	sort.Sort(APIVersionsByID(availableApiVersions)) | ||||||
|  | 	for _, status := range []string{"CURRENT", "SUPPORTED"} { | ||||||
|  | 		for _, version := range availableApiVersions { | ||||||
|  | 			if strings.ToUpper(version.Status) == status { | ||||||
|  | 				if detectedApiVersion := autoVersionSelector(&version); detectedApiVersion != "" { | ||||||
|  | 					glog.V(3).Infof("Blockstorage API version probing has found a suitable %s api version: %s", status, detectedApiVersion) | ||||||
|  | 					return detectedApiVersion | ||||||
|  | 				} | ||||||
|  | 			} | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	return "" | ||||||
|  |  | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (os *OpenStack) volumeService(forceVersion string) (volumeService, error) { | ||||||
|  | 	bsVersion := "" | ||||||
|  | 	if forceVersion == "" { | ||||||
|  | 		bsVersion = os.bsOpts.BSVersion | ||||||
|  | 	} else { | ||||||
|  | 		bsVersion = forceVersion | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	switch bsVersion { | ||||||
|  | 	case "v1": | ||||||
|  | 		sClient, err := openstack.NewBlockStorageV1(os.provider, gophercloud.EndpointOpts{ | ||||||
|  | 			Region: os.region, | ||||||
|  | 		}) | ||||||
|  | 		if err != nil || sClient == nil { | ||||||
|  | 			glog.Errorf("Unable to initialize cinder client for region: %s", os.region) | ||||||
|  | 			return nil, err | ||||||
|  | 		} | ||||||
|  | 		return &VolumesV1{sClient, os.bsOpts}, nil | ||||||
|  | 	case "v2": | ||||||
|  | 		sClient, err := openstack.NewBlockStorageV2(os.provider, gophercloud.EndpointOpts{ | ||||||
|  | 			Region: os.region, | ||||||
|  | 		}) | ||||||
|  | 		if err != nil || sClient == nil { | ||||||
|  | 			glog.Errorf("Unable to initialize cinder v2 client for region: %s", os.region) | ||||||
|  | 			return nil, err | ||||||
|  | 		} | ||||||
|  | 		return &VolumesV2{sClient, os.bsOpts}, nil | ||||||
|  | 	case "auto": | ||||||
|  | 		sClient, err := openstack.NewBlockStorageV1(os.provider, gophercloud.EndpointOpts{ | ||||||
|  | 			Region: os.region, | ||||||
|  | 		}) | ||||||
|  | 		if err != nil || sClient == nil { | ||||||
|  | 			glog.Errorf("Unable to initialize cinder client for region: %s", os.region) | ||||||
|  | 			return nil, err | ||||||
|  | 		} | ||||||
|  | 		availableApiVersions := []apiversions_v1.APIVersion{} | ||||||
|  | 		err = apiversions_v1.List(sClient).EachPage(func(page pagination.Page) (bool, error) { | ||||||
|  | 			// returning false from this handler stops page iteration, error is propagated to the upper function | ||||||
|  | 			apiversions, err := apiversions_v1.ExtractAPIVersions(page) | ||||||
|  | 			if err != nil { | ||||||
|  | 				glog.Errorf("Unable to extract api versions from page: %v", err) | ||||||
|  | 				return false, err | ||||||
|  | 			} | ||||||
|  | 			availableApiVersions = append(availableApiVersions, apiversions...) | ||||||
|  | 			return true, nil | ||||||
|  | 		}) | ||||||
|  |  | ||||||
|  | 		if err != nil { | ||||||
|  | 			glog.Errorf("Error when retrieving list of supported blockstorage api versions: %v", err) | ||||||
|  | 			return nil, err | ||||||
|  | 		} | ||||||
|  | 		if autodetectedVersion := doBsApiVersionAutodetect(availableApiVersions); autodetectedVersion != "" { | ||||||
|  | 			return os.volumeService(autodetectedVersion) | ||||||
|  | 		} else { | ||||||
|  | 			// Nothing suitable found, failed autodetection | ||||||
|  | 			return nil, errors.New("BS API version autodetection failed.") | ||||||
|  | 		} | ||||||
|  |  | ||||||
|  | 	default: | ||||||
|  | 		err_txt := fmt.Sprintf("Config error: unrecognised bs-version \"%v\"", os.bsOpts.BSVersion) | ||||||
|  | 		glog.Warningf(err_txt) | ||||||
|  | 		return nil, errors.New(err_txt) | ||||||
|  | 	} | ||||||
|  | } | ||||||
|   | |||||||
| @@ -17,6 +17,10 @@ limitations under the License. | |||||||
| package openstack | package openstack | ||||||
|  |  | ||||||
| import ( | import ( | ||||||
|  | 	"fmt" | ||||||
|  | 	"github.com/gophercloud/gophercloud" | ||||||
|  | 	"github.com/gophercloud/gophercloud/openstack/blockstorage/v1/apiversions" | ||||||
|  | 	"github.com/gophercloud/gophercloud/openstack/compute/v2/servers" | ||||||
| 	"os" | 	"os" | ||||||
| 	"reflect" | 	"reflect" | ||||||
| 	"sort" | 	"sort" | ||||||
| @@ -24,9 +28,6 @@ import ( | |||||||
| 	"testing" | 	"testing" | ||||||
| 	"time" | 	"time" | ||||||
|  |  | ||||||
| 	"github.com/gophercloud/gophercloud" |  | ||||||
| 	"github.com/gophercloud/gophercloud/openstack/compute/v2/servers" |  | ||||||
|  |  | ||||||
| 	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" | 	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" | ||||||
| 	"k8s.io/apimachinery/pkg/util/rand" | 	"k8s.io/apimachinery/pkg/util/rand" | ||||||
| 	"k8s.io/kubernetes/pkg/api/v1" | 	"k8s.io/kubernetes/pkg/api/v1" | ||||||
| @@ -81,7 +82,9 @@ func TestReadConfig(t *testing.T) { | |||||||
|  monitor-timeout = 30s |  monitor-timeout = 30s | ||||||
|  monitor-max-retries = 3 |  monitor-max-retries = 3 | ||||||
|  [BlockStorage] |  [BlockStorage] | ||||||
|  |  bs-version = auto | ||||||
|  trust-device-path = yes |  trust-device-path = yes | ||||||
|  |  | ||||||
|  `)) |  `)) | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
| 		t.Fatalf("Should succeed when a valid config is provided: %s", err) | 		t.Fatalf("Should succeed when a valid config is provided: %s", err) | ||||||
| @@ -105,6 +108,9 @@ func TestReadConfig(t *testing.T) { | |||||||
| 	if cfg.BlockStorage.TrustDevicePath != true { | 	if cfg.BlockStorage.TrustDevicePath != true { | ||||||
| 		t.Errorf("incorrect bs.trustdevicepath: %v", cfg.BlockStorage.TrustDevicePath) | 		t.Errorf("incorrect bs.trustdevicepath: %v", cfg.BlockStorage.TrustDevicePath) | ||||||
| 	} | 	} | ||||||
|  | 	if cfg.BlockStorage.BSVersion != "auto" { | ||||||
|  | 		t.Errorf("incorrect bs.bs-version: %v", cfg.BlockStorage.BSVersion) | ||||||
|  | 	} | ||||||
| } | } | ||||||
|  |  | ||||||
| func TestToAuthOptions(t *testing.T) { | func TestToAuthOptions(t *testing.T) { | ||||||
| @@ -385,3 +391,45 @@ func TestVolumes(t *testing.T) { | |||||||
| 	t.Logf("Volume (%s) deleted\n", vol) | 	t.Logf("Volume (%s) deleted\n", vol) | ||||||
|  |  | ||||||
| } | } | ||||||
|  |  | ||||||
|  | func TestCinderAutoDetectApiVersion(t *testing.T) { | ||||||
|  | 	updated := "" // not relevant to this test, can be set to any value | ||||||
|  | 	status_current := "CURRENT" | ||||||
|  | 	status_supported := "SUPpORTED" // lowercase to test regression resitance if api returns different case | ||||||
|  | 	status_deprecated := "DEPRECATED" | ||||||
|  |  | ||||||
|  | 	var result_version, api_version [4]string | ||||||
|  |  | ||||||
|  | 	for ver := 0; ver <= 3; ver++ { | ||||||
|  | 		api_version[ver] = fmt.Sprintf("v%d.0", ver) | ||||||
|  | 		result_version[ver] = fmt.Sprintf("v%d", ver) | ||||||
|  | 	} | ||||||
|  | 	result_version[0] = "" | ||||||
|  | 	api_current_v1 := apiversions.APIVersion{ID: api_version[1], Status: status_current, Updated: updated} | ||||||
|  | 	api_current_v2 := apiversions.APIVersion{ID: api_version[2], Status: status_current, Updated: updated} | ||||||
|  | 	api_current_v3 := apiversions.APIVersion{ID: api_version[3], Status: status_current, Updated: updated} | ||||||
|  |  | ||||||
|  | 	api_supported_v1 := apiversions.APIVersion{ID: api_version[1], Status: status_supported, Updated: updated} | ||||||
|  | 	api_supported_v2 := apiversions.APIVersion{ID: api_version[2], Status: status_supported, Updated: updated} | ||||||
|  |  | ||||||
|  | 	api_deprecated_v1 := apiversions.APIVersion{ID: api_version[1], Status: status_deprecated, Updated: updated} | ||||||
|  | 	api_deprecated_v2 := apiversions.APIVersion{ID: api_version[2], Status: status_deprecated, Updated: updated} | ||||||
|  |  | ||||||
|  | 	var testCases = []struct { | ||||||
|  | 		test_case     []apiversions.APIVersion | ||||||
|  | 		wanted_result string | ||||||
|  | 	}{ | ||||||
|  | 		{[]apiversions.APIVersion{api_current_v1}, result_version[1]}, | ||||||
|  | 		{[]apiversions.APIVersion{api_current_v2}, result_version[2]}, | ||||||
|  | 		{[]apiversions.APIVersion{api_supported_v1, api_current_v2}, result_version[2]},                     // current always selected | ||||||
|  | 		{[]apiversions.APIVersion{api_current_v1, api_supported_v2}, result_version[1]},                     // current always selected | ||||||
|  | 		{[]apiversions.APIVersion{api_current_v3, api_supported_v2, api_deprecated_v1}, result_version[2]},  // with current v3, but should fall back to v2 | ||||||
|  | 		{[]apiversions.APIVersion{api_current_v3, api_deprecated_v2, api_deprecated_v1}, result_version[0]}, // v3 is not supported | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	for _, suite := range testCases { | ||||||
|  | 		if autodetectedVersion := doBsApiVersionAutodetect(suite.test_case); autodetectedVersion != suite.wanted_result { | ||||||
|  | 			t.Fatalf("Autodetect for suite: %s, failed with result: '%s', wanted '%s'", suite.test_case, autodetectedVersion, suite.wanted_result) | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | } | ||||||
|   | |||||||
| @@ -23,100 +23,96 @@ import ( | |||||||
| 	"path" | 	"path" | ||||||
| 	"strings" | 	"strings" | ||||||
|  |  | ||||||
| 	"k8s.io/kubernetes/pkg/volume" | 	k8s_volume "k8s.io/kubernetes/pkg/volume" | ||||||
|  |  | ||||||
| 	"github.com/gophercloud/gophercloud" | 	"github.com/gophercloud/gophercloud" | ||||||
| 	"github.com/gophercloud/gophercloud/openstack" | 	"github.com/gophercloud/gophercloud/openstack" | ||||||
| 	"github.com/gophercloud/gophercloud/openstack/blockstorage/v1/volumes" | 	volumes_v1 "github.com/gophercloud/gophercloud/openstack/blockstorage/v1/volumes" | ||||||
|  | 	volumes_v2 "github.com/gophercloud/gophercloud/openstack/blockstorage/v2/volumes" | ||||||
| 	"github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/volumeattach" | 	"github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/volumeattach" | ||||||
| 	"github.com/gophercloud/gophercloud/pagination" | 	"github.com/gophercloud/gophercloud/pagination" | ||||||
|  |  | ||||||
| 	"github.com/golang/glog" | 	"github.com/golang/glog" | ||||||
| ) | ) | ||||||
|  |  | ||||||
| // Attaches given cinder volume to the compute running kubelet | type volumeService interface { | ||||||
| func (os *OpenStack) AttachDisk(instanceID string, diskName string) (string, error) { | 	createVolume(opts VolumeCreateOpts) (string, error) | ||||||
| 	disk, err := os.getVolume(diskName) | 	getVolume(diskName string) (Volume, error) | ||||||
| 	if err != nil { | 	deleteVolume(volumeName string) error | ||||||
| 		return "", err |  | ||||||
| 	} |  | ||||||
| 	cClient, err := openstack.NewComputeV2(os.provider, gophercloud.EndpointOpts{ |  | ||||||
| 		Region: os.region, |  | ||||||
| 	}) |  | ||||||
| 	if err != nil || cClient == nil { |  | ||||||
| 		glog.Errorf("Unable to initialize nova client for region: %s", os.region) |  | ||||||
| 		return "", err |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	if len(disk.Attachments) > 0 && disk.Attachments[0]["server_id"] != nil { |  | ||||||
| 		if instanceID == disk.Attachments[0]["server_id"] { |  | ||||||
| 			glog.V(4).Infof("Disk: %q is already attached to compute: %q", diskName, instanceID) |  | ||||||
| 			return disk.ID, nil |  | ||||||
| 		} |  | ||||||
|  |  | ||||||
| 		glog.V(2).Infof("Disk %q is attached to a different compute (%q), detaching", diskName, disk.Attachments[0]["server_id"]) |  | ||||||
| 		err = os.DetachDisk(fmt.Sprintf("%s", disk.Attachments[0]["server_id"]), diskName) |  | ||||||
| 		if err != nil { |  | ||||||
| 			return "", err |  | ||||||
| 		} |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	// add read only flag here if possible spothanis |  | ||||||
| 	_, err = volumeattach.Create(cClient, instanceID, &volumeattach.CreateOpts{ |  | ||||||
| 		VolumeID: disk.ID, |  | ||||||
| 	}).Extract() |  | ||||||
| 	if err != nil { |  | ||||||
| 		glog.Errorf("Failed to attach %s volume to %s compute: %v", diskName, instanceID, err) |  | ||||||
| 		return "", err |  | ||||||
| 	} |  | ||||||
| 	glog.V(2).Infof("Successfully attached %s volume to %s compute", diskName, instanceID) |  | ||||||
| 	return disk.ID, nil |  | ||||||
| } | } | ||||||
|  |  | ||||||
| // Detaches given cinder volume from the compute running kubelet | // Volumes implementation for v1 | ||||||
| func (os *OpenStack) DetachDisk(instanceID string, partialDiskId string) error { | type VolumesV1 struct { | ||||||
| 	disk, err := os.getVolume(partialDiskId) | 	blockstorage *gophercloud.ServiceClient | ||||||
| 	if err != nil { | 	opts         BlockStorageOpts | ||||||
| 		return err |  | ||||||
| 	} |  | ||||||
| 	cClient, err := openstack.NewComputeV2(os.provider, gophercloud.EndpointOpts{ |  | ||||||
| 		Region: os.region, |  | ||||||
| 	}) |  | ||||||
| 	if err != nil || cClient == nil { |  | ||||||
| 		glog.Errorf("Unable to initialize nova client for region: %s", os.region) |  | ||||||
| 		return err |  | ||||||
| 	} |  | ||||||
| 	if len(disk.Attachments) > 0 && disk.Attachments[0]["server_id"] != nil && instanceID == disk.Attachments[0]["server_id"] { |  | ||||||
| 		// This is a blocking call and effects kubelet's performance directly. |  | ||||||
| 		// We should consider kicking it out into a separate routine, if it is bad. |  | ||||||
| 		err = volumeattach.Delete(cClient, instanceID, disk.ID).ExtractErr() |  | ||||||
| 		if err != nil { |  | ||||||
| 			glog.Errorf("Failed to delete volume %s from compute %s attached %v", disk.ID, instanceID, err) |  | ||||||
| 			return err |  | ||||||
| 		} |  | ||||||
| 		glog.V(2).Infof("Successfully detached volume: %s from compute: %s", disk.ID, instanceID) |  | ||||||
| 	} else { |  | ||||||
| 		errMsg := fmt.Sprintf("Disk: %s has no attachments or is not attached to compute: %s", disk.Name, instanceID) |  | ||||||
| 		glog.Errorf(errMsg) |  | ||||||
| 		return errors.New(errMsg) |  | ||||||
| 	} |  | ||||||
| 	return nil |  | ||||||
| } | } | ||||||
|  |  | ||||||
| // Takes a partial/full disk id or diskname | // Volumes implementation for v2 | ||||||
| func (os *OpenStack) getVolume(diskName string) (volumes.Volume, error) { | type VolumesV2 struct { | ||||||
| 	sClient, err := openstack.NewBlockStorageV1(os.provider, gophercloud.EndpointOpts{ | 	blockstorage *gophercloud.ServiceClient | ||||||
| 		Region: os.region, | 	opts         BlockStorageOpts | ||||||
| 	}) | } | ||||||
|  |  | ||||||
| 	var volume volumes.Volume | type Volume struct { | ||||||
| 	if err != nil || sClient == nil { | 	// ID of the instance, to which this volume is attached. "" if not attached | ||||||
| 		glog.Errorf("Unable to initialize cinder client for region: %s", os.region) | 	AttachedServerId string | ||||||
| 		return volume, err | 	// Device file path | ||||||
|  | 	AttachedDevice string | ||||||
|  | 	// Unique identifier for the volume. | ||||||
|  | 	ID string | ||||||
|  | 	// Human-readable display name for the volume. | ||||||
|  | 	Name string | ||||||
|  | 	// Current status of the volume. | ||||||
|  | 	Status string | ||||||
|  | } | ||||||
|  |  | ||||||
|  | type VolumeCreateOpts struct { | ||||||
|  | 	Size         int | ||||||
|  | 	Availability string | ||||||
|  | 	Name         string | ||||||
|  | 	VolumeType   string | ||||||
|  | 	Metadata     map[string]string | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (volumes *VolumesV1) createVolume(opts VolumeCreateOpts) (string, error) { | ||||||
|  |  | ||||||
|  | 	create_opts := volumes_v1.CreateOpts{ | ||||||
|  | 		Name:         opts.Name, | ||||||
|  | 		Size:         opts.Size, | ||||||
|  | 		VolumeType:   opts.VolumeType, | ||||||
|  | 		Availability: opts.Availability, | ||||||
|  | 		Metadata:     opts.Metadata, | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	err = volumes.List(sClient, nil).EachPage(func(page pagination.Page) (bool, error) { | 	vol, err := volumes_v1.Create(volumes.blockstorage, create_opts).Extract() | ||||||
| 		vols, err := volumes.ExtractVolumes(page) | 	if err != nil { | ||||||
|  | 		return "", err | ||||||
|  | 	} | ||||||
|  | 	return vol.ID, nil | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (volumes *VolumesV2) createVolume(opts VolumeCreateOpts) (string, error) { | ||||||
|  |  | ||||||
|  | 	create_opts := volumes_v2.CreateOpts{ | ||||||
|  | 		Name:             opts.Name, | ||||||
|  | 		Size:             opts.Size, | ||||||
|  | 		VolumeType:       opts.VolumeType, | ||||||
|  | 		AvailabilityZone: opts.Availability, | ||||||
|  | 		Metadata:         opts.Metadata, | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	vol, err := volumes_v2.Create(volumes.blockstorage, create_opts).Extract() | ||||||
|  | 	if err != nil { | ||||||
|  | 		return "", err | ||||||
|  | 	} | ||||||
|  | 	return vol.ID, nil | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (volumes *VolumesV1) getVolume(diskName string) (Volume, error) { | ||||||
|  | 	var volume_v1 volumes_v1.Volume | ||||||
|  | 	var volume Volume | ||||||
|  | 	err := volumes_v1.List(volumes.blockstorage, nil).EachPage(func(page pagination.Page) (bool, error) { | ||||||
|  | 		vols, err := volumes_v1.ExtractVolumes(page) | ||||||
| 		if err != nil { | 		if err != nil { | ||||||
| 			glog.Errorf("Failed to extract volumes: %v", err) | 			glog.Errorf("Failed to extract volumes: %v", err) | ||||||
| 			return false, err | 			return false, err | ||||||
| @@ -124,35 +120,177 @@ func (os *OpenStack) getVolume(diskName string) (volumes.Volume, error) { | |||||||
| 			for _, v := range vols { | 			for _, v := range vols { | ||||||
| 				glog.V(4).Infof("%s %s %v", v.ID, v.Name, v.Attachments) | 				glog.V(4).Infof("%s %s %v", v.ID, v.Name, v.Attachments) | ||||||
| 				if v.Name == diskName || strings.Contains(v.ID, diskName) { | 				if v.Name == diskName || strings.Contains(v.ID, diskName) { | ||||||
| 					volume = v | 					volume_v1 = v | ||||||
| 					return true, nil | 					return true, nil | ||||||
| 				} | 				} | ||||||
| 			} | 			} | ||||||
| 		} | 		} | ||||||
| 		// if it reached here then no disk with the given name was found. | 		// if it reached here then no disk with the given name was found. | ||||||
| 		errmsg := fmt.Sprintf("Unable to find disk: %s in region %s", diskName, os.region) | 		errmsg := fmt.Sprintf("Unable to find disk: %s", diskName) | ||||||
| 		return false, errors.New(errmsg) | 		return false, errors.New(errmsg) | ||||||
| 	}) | 	}) | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
| 		glog.Errorf("Error occurred getting volume: %s", diskName) | 		glog.Errorf("Error occurred getting volume: %s", diskName) | ||||||
| 		return volume, err | 		return volume, err | ||||||
| 	} | 	} | ||||||
| 	return volume, err |  | ||||||
|  | 	volume.ID = volume_v1.ID | ||||||
|  | 	volume.Name = volume_v1.Name | ||||||
|  | 	volume.Status = volume_v1.Status | ||||||
|  |  | ||||||
|  | 	if len(volume_v1.Attachments) > 0 && volume_v1.Attachments[0]["server_id"] != nil { | ||||||
|  | 		volume.AttachedServerId = volume_v1.Attachments[0]["server_id"].(string) | ||||||
|  | 		volume.AttachedDevice = volume_v1.Attachments[0]["device"].(string) | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	return volume, nil | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (volumes *VolumesV2) getVolume(diskName string) (Volume, error) { | ||||||
|  | 	var volume_v2 volumes_v2.Volume | ||||||
|  | 	var volume Volume | ||||||
|  | 	err := volumes_v2.List(volumes.blockstorage, nil).EachPage(func(page pagination.Page) (bool, error) { | ||||||
|  | 		vols, err := volumes_v2.ExtractVolumes(page) | ||||||
|  | 		if err != nil { | ||||||
|  | 			glog.Errorf("Failed to extract volumes: %v", err) | ||||||
|  | 			return false, err | ||||||
|  | 		} else { | ||||||
|  | 			for _, v := range vols { | ||||||
|  | 				glog.V(4).Infof("%s %s %v", v.ID, v.Name, v.Attachments) | ||||||
|  | 				if v.Name == diskName || strings.Contains(v.ID, diskName) { | ||||||
|  | 					volume_v2 = v | ||||||
|  | 					return true, nil | ||||||
|  | 				} | ||||||
|  | 			} | ||||||
|  | 		} | ||||||
|  | 		// if it reached here then no disk with the given name was found. | ||||||
|  | 		errmsg := fmt.Sprintf("Unable to find disk: %s", diskName) | ||||||
|  | 		return false, errors.New(errmsg) | ||||||
|  | 	}) | ||||||
|  | 	if err != nil { | ||||||
|  | 		glog.Errorf("Error occurred getting volume: %s", diskName) | ||||||
|  | 		return volume, err | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	volume.ID = volume_v2.ID | ||||||
|  | 	volume.Name = volume_v2.Name | ||||||
|  | 	volume.Status = volume_v2.Status | ||||||
|  |  | ||||||
|  | 	if len(volume_v2.Attachments) > 0 { | ||||||
|  | 		volume.AttachedServerId = volume_v2.Attachments[0].ServerID | ||||||
|  | 		volume.AttachedDevice = volume_v2.Attachments[0].Device | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	return volume, nil | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (volumes *VolumesV1) deleteVolume(volumeName string) error { | ||||||
|  |  | ||||||
|  | 	err := volumes_v1.Delete(volumes.blockstorage, volumeName).ExtractErr() | ||||||
|  | 	if err != nil { | ||||||
|  | 		glog.Errorf("Cannot delete volume %s: %v", volumeName, err) | ||||||
|  | 	} | ||||||
|  | 	return err | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (volumes *VolumesV2) deleteVolume(volumeName string) error { | ||||||
|  | 	err := volumes_v2.Delete(volumes.blockstorage, volumeName).ExtractErr() | ||||||
|  | 	if err != nil { | ||||||
|  | 		glog.Errorf("Cannot delete volume %s: %v", volumeName, err) | ||||||
|  | 	} | ||||||
|  | 	return err | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // Attaches given cinder volume to the compute running kubelet | ||||||
|  | func (os *OpenStack) AttachDisk(instanceID string, diskName string) (string, error) { | ||||||
|  | 	volume, err := os.getVolume(diskName) | ||||||
|  | 	if err != nil { | ||||||
|  | 		return "", err | ||||||
|  | 	} | ||||||
|  | 	cClient, err := openstack.NewComputeV2(os.provider, gophercloud.EndpointOpts{ | ||||||
|  | 		Region: os.region, | ||||||
|  | 	}) | ||||||
|  | 	if err != nil || cClient == nil { | ||||||
|  | 		glog.Errorf("Unable to initialize nova client for region: %s", os.region) | ||||||
|  | 		return "", err | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	if volume.AttachedServerId != "" { | ||||||
|  | 		if instanceID == volume.AttachedServerId { | ||||||
|  | 			glog.V(4).Infof("Disk: %q is already attached to compute: %q", diskName, instanceID) | ||||||
|  | 			return volume.ID, nil | ||||||
|  | 		} | ||||||
|  | 		glog.V(2).Infof("Disk %q is attached to a different compute (%q), detaching", diskName, volume.AttachedServerId) | ||||||
|  | 		err = os.DetachDisk(volume.AttachedServerId, diskName) | ||||||
|  | 		if err != nil { | ||||||
|  | 			return "", err | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	// add read only flag here if possible spothanis | ||||||
|  | 	_, err = volumeattach.Create(cClient, instanceID, &volumeattach.CreateOpts{ | ||||||
|  | 		VolumeID: volume.ID, | ||||||
|  | 	}).Extract() | ||||||
|  | 	if err != nil { | ||||||
|  | 		glog.Errorf("Failed to attach %s volume to %s compute: %v", diskName, instanceID, err) | ||||||
|  | 		return "", err | ||||||
|  | 	} | ||||||
|  | 	glog.V(2).Infof("Successfully attached %s volume to %s compute", diskName, instanceID) | ||||||
|  | 	return volume.ID, nil | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // Detaches given cinder volume from the compute running kubelet | ||||||
|  | func (os *OpenStack) DetachDisk(instanceID string, partialDiskId string) error { | ||||||
|  | 	volume, err := os.getVolume(partialDiskId) | ||||||
|  | 	if err != nil { | ||||||
|  | 		return err | ||||||
|  | 	} | ||||||
|  | 	cClient, err := openstack.NewComputeV2(os.provider, gophercloud.EndpointOpts{ | ||||||
|  | 		Region: os.region, | ||||||
|  | 	}) | ||||||
|  | 	if err != nil || cClient == nil { | ||||||
|  | 		glog.Errorf("Unable to initialize nova client for region: %s", os.region) | ||||||
|  | 		return err | ||||||
|  | 	} | ||||||
|  | 	if volume.AttachedServerId != instanceID { | ||||||
|  | 		errMsg := fmt.Sprintf("Disk: %s has no attachments or is not attached to compute: %s", volume.Name, instanceID) | ||||||
|  | 		glog.Errorf(errMsg) | ||||||
|  | 		return errors.New(errMsg) | ||||||
|  | 	} else { | ||||||
|  | 		// This is a blocking call and effects kubelet's performance directly. | ||||||
|  | 		// We should consider kicking it out into a separate routine, if it is bad. | ||||||
|  | 		err = volumeattach.Delete(cClient, instanceID, volume.ID).ExtractErr() | ||||||
|  | 		if err != nil { | ||||||
|  | 			glog.Errorf("Failed to delete volume %s from compute %s attached %v", volume.ID, instanceID, err) | ||||||
|  | 			return err | ||||||
|  | 		} | ||||||
|  | 		glog.V(2).Infof("Successfully detached volume: %s from compute: %s", volume.ID, instanceID) | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	return nil | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // Takes a partial/full disk id or diskname | ||||||
|  | func (os *OpenStack) getVolume(diskName string) (Volume, error) { | ||||||
|  |  | ||||||
|  | 	volumes, err := os.volumeService("") | ||||||
|  | 	if err != nil || volumes == nil { | ||||||
|  | 		glog.Errorf("Unable to initialize cinder client for region: %s", os.region) | ||||||
|  | 		return Volume{}, err | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	return volumes.getVolume(diskName) | ||||||
| } | } | ||||||
|  |  | ||||||
| // Create a volume of given size (in GiB) | // Create a volume of given size (in GiB) | ||||||
| func (os *OpenStack) CreateVolume(name string, size int, vtype, availability string, tags *map[string]string) (volumeName string, err error) { | func (os *OpenStack) CreateVolume(name string, size int, vtype, availability string, tags *map[string]string) (volumeName string, err error) { | ||||||
|  |  | ||||||
| 	sClient, err := openstack.NewBlockStorageV1(os.provider, gophercloud.EndpointOpts{ | 	volumes, err := os.volumeService("") | ||||||
| 		Region: os.region, | 	if err != nil || volumes == nil { | ||||||
| 	}) |  | ||||||
|  |  | ||||||
| 	if err != nil || sClient == nil { |  | ||||||
| 		glog.Errorf("Unable to initialize cinder client for region: %s", os.region) | 		glog.Errorf("Unable to initialize cinder client for region: %s", os.region) | ||||||
| 		return "", err | 		return "", err | ||||||
| 	} | 	} | ||||||
|  | 	opts := VolumeCreateOpts{ | ||||||
| 	opts := volumes.CreateOpts{ |  | ||||||
| 		Name:         name, | 		Name:         name, | ||||||
| 		Size:         size, | 		Size:         size, | ||||||
| 		VolumeType:   vtype, | 		VolumeType:   vtype, | ||||||
| @@ -161,13 +299,15 @@ func (os *OpenStack) CreateVolume(name string, size int, vtype, availability str | |||||||
| 	if tags != nil { | 	if tags != nil { | ||||||
| 		opts.Metadata = *tags | 		opts.Metadata = *tags | ||||||
| 	} | 	} | ||||||
| 	vol, err := volumes.Create(sClient, opts).Extract() | 	volume_id, err := volumes.createVolume(opts) | ||||||
|  |  | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
| 		glog.Errorf("Failed to create a %d GB volume: %v", size, err) | 		glog.Errorf("Failed to create a %d GB volume: %v", size, err) | ||||||
| 		return "", err | 		return "", err | ||||||
| 	} | 	} | ||||||
| 	glog.Infof("Created volume %v", vol.ID) |  | ||||||
| 	return vol.ID, err | 	glog.Infof("Created volume %v", volume_id) | ||||||
|  | 	return volume_id, nil | ||||||
| } | } | ||||||
|  |  | ||||||
| // GetDevicePath returns the path of an attached block storage volume, specified by its id. | // GetDevicePath returns the path of an attached block storage volume, specified by its id. | ||||||
| @@ -202,39 +342,38 @@ func (os *OpenStack) DeleteVolume(volumeName string) error { | |||||||
| 	} | 	} | ||||||
| 	if used { | 	if used { | ||||||
| 		msg := fmt.Sprintf("Cannot delete the volume %q, it's still attached to a node", volumeName) | 		msg := fmt.Sprintf("Cannot delete the volume %q, it's still attached to a node", volumeName) | ||||||
| 		return volume.NewDeletedVolumeInUseError(msg) | 		return k8s_volume.NewDeletedVolumeInUseError(msg) | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	sClient, err := openstack.NewBlockStorageV1(os.provider, gophercloud.EndpointOpts{ | 	volumes, err := os.volumeService("") | ||||||
| 		Region: os.region, | 	if err != nil || volumes == nil { | ||||||
| 	}) |  | ||||||
|  |  | ||||||
| 	if err != nil || sClient == nil { |  | ||||||
| 		glog.Errorf("Unable to initialize cinder client for region: %s", os.region) | 		glog.Errorf("Unable to initialize cinder client for region: %s", os.region) | ||||||
| 		return err | 		return err | ||||||
| 	} | 	} | ||||||
| 	err = volumes.Delete(sClient, volumeName).ExtractErr() |  | ||||||
|  | 	err = volumes.deleteVolume(volumeName) | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
| 		glog.Errorf("Cannot delete volume %s: %v", volumeName, err) | 		glog.Errorf("Cannot delete volume %s: %v", volumeName, err) | ||||||
| 	} | 	} | ||||||
| 	return err | 	return nil | ||||||
|  |  | ||||||
| } | } | ||||||
|  |  | ||||||
| // Get device path of attached volume to the compute running kubelet, as known by cinder | // Get device path of attached volume to the compute running kubelet, as known by cinder | ||||||
| func (os *OpenStack) GetAttachmentDiskPath(instanceID string, diskName string) (string, error) { | func (os *OpenStack) GetAttachmentDiskPath(instanceID string, diskName string) (string, error) { | ||||||
| 	// See issue #33128 - Cinder does not always tell you the right device path, as such | 	// See issue #33128 - Cinder does not always tell you the right device path, as such | ||||||
| 	// we must only use this value as a last resort. | 	// we must only use this value as a last resort. | ||||||
| 	disk, err := os.getVolume(diskName) | 	volume, err := os.getVolume(diskName) | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
| 		return "", err | 		return "", err | ||||||
| 	} | 	} | ||||||
| 	if len(disk.Attachments) > 0 && disk.Attachments[0]["server_id"] != nil { | 	if volume.AttachedServerId != "" { | ||||||
| 		if instanceID == disk.Attachments[0]["server_id"] { | 		if instanceID == volume.AttachedServerId { | ||||||
| 			// Attachment[0]["device"] points to the device path | 			// Attachment[0]["device"] points to the device path | ||||||
| 			// see http://developer.openstack.org/api-ref-blockstorage-v1.html | 			// see http://developer.openstack.org/api-ref-blockstorage-v1.html | ||||||
| 			return disk.Attachments[0]["device"].(string), nil | 			return volume.AttachedDevice, nil | ||||||
| 		} else { | 		} else { | ||||||
| 			errMsg := fmt.Sprintf("Disk %q is attached to a different compute: %q, should be detached before proceeding", diskName, disk.Attachments[0]["server_id"]) | 			errMsg := fmt.Sprintf("Disk %q is attached to a different compute: %q, should be detached before proceeding", diskName, volume.AttachedServerId) | ||||||
| 			glog.Errorf(errMsg) | 			glog.Errorf(errMsg) | ||||||
| 			return "", errors.New(errMsg) | 			return "", errors.New(errMsg) | ||||||
| 		} | 		} | ||||||
| @@ -244,11 +383,12 @@ func (os *OpenStack) GetAttachmentDiskPath(instanceID string, diskName string) ( | |||||||
|  |  | ||||||
| // query if a volume is attached to a compute instance | // query if a volume is attached to a compute instance | ||||||
| func (os *OpenStack) DiskIsAttached(diskName, instanceID string) (bool, error) { | func (os *OpenStack) DiskIsAttached(diskName, instanceID string) (bool, error) { | ||||||
| 	disk, err := os.getVolume(diskName) | 	volume, err := os.getVolume(diskName) | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
| 		return false, err | 		return false, err | ||||||
| 	} | 	} | ||||||
| 	if len(disk.Attachments) > 0 && disk.Attachments[0]["server_id"] != nil && instanceID == disk.Attachments[0]["server_id"] { |  | ||||||
|  | 	if instanceID == volume.AttachedServerId { | ||||||
| 		return true, nil | 		return true, nil | ||||||
| 	} | 	} | ||||||
| 	return false, nil | 	return false, nil | ||||||
| @@ -258,27 +398,19 @@ func (os *OpenStack) DiskIsAttached(diskName, instanceID string) (bool, error) { | |||||||
| func (os *OpenStack) DisksAreAttached(diskNames []string, instanceID string) (map[string]bool, error) { | func (os *OpenStack) DisksAreAttached(diskNames []string, instanceID string) (map[string]bool, error) { | ||||||
| 	attached := make(map[string]bool) | 	attached := make(map[string]bool) | ||||||
| 	for _, diskName := range diskNames { | 	for _, diskName := range diskNames { | ||||||
| 		attached[diskName] = false | 		is_attached, _ := os.DiskIsAttached(diskName, instanceID) | ||||||
| 	} | 		attached[diskName] = is_attached | ||||||
| 	for _, diskName := range diskNames { |  | ||||||
| 		disk, err := os.getVolume(diskName) |  | ||||||
| 		if err != nil { |  | ||||||
| 			continue |  | ||||||
| 		} |  | ||||||
| 		if len(disk.Attachments) > 0 && disk.Attachments[0]["server_id"] != nil && instanceID == disk.Attachments[0]["server_id"] { |  | ||||||
| 			attached[diskName] = true |  | ||||||
| 		} |  | ||||||
| 	} | 	} | ||||||
| 	return attached, nil | 	return attached, nil | ||||||
| } | } | ||||||
|  |  | ||||||
| // diskIsUsed returns true a disk is attached to any node. | // diskIsUsed returns true a disk is attached to any node. | ||||||
| func (os *OpenStack) diskIsUsed(diskName string) (bool, error) { | func (os *OpenStack) diskIsUsed(diskName string) (bool, error) { | ||||||
| 	disk, err := os.getVolume(diskName) | 	volume, err := os.getVolume(diskName) | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
| 		return false, err | 		return false, err | ||||||
| 	} | 	} | ||||||
| 	if len(disk.Attachments) > 0 { | 	if volume.AttachedServerId != "" { | ||||||
| 		return true, nil | 		return true, nil | ||||||
| 	} | 	} | ||||||
| 	return false, nil | 	return false, nil | ||||||
|   | |||||||
							
								
								
									
										31
									
								
								vendor/BUILD
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										31
									
								
								vendor/BUILD
									
									
									
									
										vendored
									
									
								
							| @@ -4160,6 +4160,21 @@ go_library( | |||||||
|     ], |     ], | ||||||
| ) | ) | ||||||
|  |  | ||||||
|  | go_library( | ||||||
|  |     name = "github.com/gophercloud/gophercloud/openstack/blockstorage/v1/apiversions", | ||||||
|  |     srcs = [ | ||||||
|  |         "github.com/gophercloud/gophercloud/openstack/blockstorage/v1/apiversions/doc.go", | ||||||
|  |         "github.com/gophercloud/gophercloud/openstack/blockstorage/v1/apiversions/requests.go", | ||||||
|  |         "github.com/gophercloud/gophercloud/openstack/blockstorage/v1/apiversions/results.go", | ||||||
|  |         "github.com/gophercloud/gophercloud/openstack/blockstorage/v1/apiversions/urls.go", | ||||||
|  |     ], | ||||||
|  |     tags = ["automanaged"], | ||||||
|  |     deps = [ | ||||||
|  |         "//vendor:github.com/gophercloud/gophercloud", | ||||||
|  |         "//vendor:github.com/gophercloud/gophercloud/pagination", | ||||||
|  |     ], | ||||||
|  | ) | ||||||
|  |  | ||||||
| go_library( | go_library( | ||||||
|     name = "github.com/gophercloud/gophercloud/openstack/blockstorage/v1/volumes", |     name = "github.com/gophercloud/gophercloud/openstack/blockstorage/v1/volumes", | ||||||
|     srcs = [ |     srcs = [ | ||||||
| @@ -4176,6 +4191,22 @@ go_library( | |||||||
|     ], |     ], | ||||||
| ) | ) | ||||||
|  |  | ||||||
|  | go_library( | ||||||
|  |     name = "github.com/gophercloud/gophercloud/openstack/blockstorage/v2/volumes", | ||||||
|  |     srcs = [ | ||||||
|  |         "github.com/gophercloud/gophercloud/openstack/blockstorage/v2/volumes/doc.go", | ||||||
|  |         "github.com/gophercloud/gophercloud/openstack/blockstorage/v2/volumes/requests.go", | ||||||
|  |         "github.com/gophercloud/gophercloud/openstack/blockstorage/v2/volumes/results.go", | ||||||
|  |         "github.com/gophercloud/gophercloud/openstack/blockstorage/v2/volumes/urls.go", | ||||||
|  |         "github.com/gophercloud/gophercloud/openstack/blockstorage/v2/volumes/util.go", | ||||||
|  |     ], | ||||||
|  |     tags = ["automanaged"], | ||||||
|  |     deps = [ | ||||||
|  |         "//vendor:github.com/gophercloud/gophercloud", | ||||||
|  |         "//vendor:github.com/gophercloud/gophercloud/pagination", | ||||||
|  |     ], | ||||||
|  | ) | ||||||
|  |  | ||||||
| go_library( | go_library( | ||||||
|     name = "github.com/gophercloud/gophercloud/openstack/common/extensions", |     name = "github.com/gophercloud/gophercloud/openstack/common/extensions", | ||||||
|     srcs = [ |     srcs = [ | ||||||
|   | |||||||
							
								
								
									
										3
									
								
								vendor/github.com/gophercloud/gophercloud/openstack/blockstorage/v1/apiversions/doc.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										3
									
								
								vendor/github.com/gophercloud/gophercloud/openstack/blockstorage/v1/apiversions/doc.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,3 @@ | |||||||
|  | // Package apiversions provides information and interaction with the different | ||||||
|  | // API versions for the OpenStack Block Storage service, code-named Cinder. | ||||||
|  | package apiversions | ||||||
							
								
								
									
										20
									
								
								vendor/github.com/gophercloud/gophercloud/openstack/blockstorage/v1/apiversions/requests.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										20
									
								
								vendor/github.com/gophercloud/gophercloud/openstack/blockstorage/v1/apiversions/requests.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,20 @@ | |||||||
|  | package apiversions | ||||||
|  |  | ||||||
|  | import ( | ||||||
|  | 	"github.com/gophercloud/gophercloud" | ||||||
|  | 	"github.com/gophercloud/gophercloud/pagination" | ||||||
|  | ) | ||||||
|  |  | ||||||
|  | // List lists all the Cinder API versions available to end-users. | ||||||
|  | func List(c *gophercloud.ServiceClient) pagination.Pager { | ||||||
|  | 	return pagination.NewPager(c, listURL(c), func(r pagination.PageResult) pagination.Page { | ||||||
|  | 		return APIVersionPage{pagination.SinglePageBase(r)} | ||||||
|  | 	}) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // Get will retrieve the volume type with the provided ID. To extract the volume | ||||||
|  | // type from the result, call the Extract method on the GetResult. | ||||||
|  | func Get(client *gophercloud.ServiceClient, v string) (r GetResult) { | ||||||
|  | 	_, r.Err = client.Get(getURL(client, v), &r.Body, nil) | ||||||
|  | 	return | ||||||
|  | } | ||||||
							
								
								
									
										49
									
								
								vendor/github.com/gophercloud/gophercloud/openstack/blockstorage/v1/apiversions/results.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										49
									
								
								vendor/github.com/gophercloud/gophercloud/openstack/blockstorage/v1/apiversions/results.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,49 @@ | |||||||
|  | package apiversions | ||||||
|  |  | ||||||
|  | import ( | ||||||
|  | 	"github.com/gophercloud/gophercloud" | ||||||
|  | 	"github.com/gophercloud/gophercloud/pagination" | ||||||
|  | ) | ||||||
|  |  | ||||||
|  | // APIVersion represents an API version for Cinder. | ||||||
|  | type APIVersion struct { | ||||||
|  | 	ID      string `json:"id"`      // unique identifier | ||||||
|  | 	Status  string `json:"status"`  // current status | ||||||
|  | 	Updated string `json:"updated"` // date last updated | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // APIVersionPage is the page returned by a pager when traversing over a | ||||||
|  | // collection of API versions. | ||||||
|  | type APIVersionPage struct { | ||||||
|  | 	pagination.SinglePageBase | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // IsEmpty checks whether an APIVersionPage struct is empty. | ||||||
|  | func (r APIVersionPage) IsEmpty() (bool, error) { | ||||||
|  | 	is, err := ExtractAPIVersions(r) | ||||||
|  | 	return len(is) == 0, err | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // ExtractAPIVersions takes a collection page, extracts all of the elements, | ||||||
|  | // and returns them a slice of APIVersion structs. It is effectively a cast. | ||||||
|  | func ExtractAPIVersions(r pagination.Page) ([]APIVersion, error) { | ||||||
|  | 	var s struct { | ||||||
|  | 		Versions []APIVersion `json:"versions"` | ||||||
|  | 	} | ||||||
|  | 	err := (r.(APIVersionPage)).ExtractInto(&s) | ||||||
|  | 	return s.Versions, err | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // GetResult represents the result of a get operation. | ||||||
|  | type GetResult struct { | ||||||
|  | 	gophercloud.Result | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // Extract is a function that accepts a result and extracts an API version resource. | ||||||
|  | func (r GetResult) Extract() (*APIVersion, error) { | ||||||
|  | 	var s struct { | ||||||
|  | 		Version *APIVersion `json:"version"` | ||||||
|  | 	} | ||||||
|  | 	err := r.ExtractInto(&s) | ||||||
|  | 	return s.Version, err | ||||||
|  | } | ||||||
							
								
								
									
										18
									
								
								vendor/github.com/gophercloud/gophercloud/openstack/blockstorage/v1/apiversions/urls.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										18
									
								
								vendor/github.com/gophercloud/gophercloud/openstack/blockstorage/v1/apiversions/urls.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,18 @@ | |||||||
|  | package apiversions | ||||||
|  |  | ||||||
|  | import ( | ||||||
|  | 	"strings" | ||||||
|  | 	"net/url" | ||||||
|  |  | ||||||
|  | 	"github.com/gophercloud/gophercloud" | ||||||
|  | ) | ||||||
|  |  | ||||||
|  | func getURL(c *gophercloud.ServiceClient, version string) string { | ||||||
|  | 	return c.ServiceURL(strings.TrimRight(version, "/") + "/") | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func listURL(c *gophercloud.ServiceClient) string { | ||||||
|  | 	u, _ := url.Parse(c.ServiceURL("")) | ||||||
|  | 	u.Path = "/" | ||||||
|  | 	return u.String() | ||||||
|  | } | ||||||
							
								
								
									
										5
									
								
								vendor/github.com/gophercloud/gophercloud/openstack/blockstorage/v2/volumes/doc.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										5
									
								
								vendor/github.com/gophercloud/gophercloud/openstack/blockstorage/v2/volumes/doc.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,5 @@ | |||||||
|  | // Package volumes provides information and interaction with volumes in the | ||||||
|  | // OpenStack Block Storage service. A volume is a detachable block storage | ||||||
|  | // device, akin to a USB hard drive. It can only be attached to one instance at | ||||||
|  | // a time. | ||||||
|  | package volumes | ||||||
							
								
								
									
										182
									
								
								vendor/github.com/gophercloud/gophercloud/openstack/blockstorage/v2/volumes/requests.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										182
									
								
								vendor/github.com/gophercloud/gophercloud/openstack/blockstorage/v2/volumes/requests.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,182 @@ | |||||||
|  | package volumes | ||||||
|  |  | ||||||
|  | import ( | ||||||
|  | 	"github.com/gophercloud/gophercloud" | ||||||
|  | 	"github.com/gophercloud/gophercloud/pagination" | ||||||
|  | ) | ||||||
|  |  | ||||||
|  | // CreateOptsBuilder allows extensions to add additional parameters to the | ||||||
|  | // Create request. | ||||||
|  | type CreateOptsBuilder interface { | ||||||
|  | 	ToVolumeCreateMap() (map[string]interface{}, error) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // CreateOpts contains options for creating a Volume. This object is passed to | ||||||
|  | // the volumes.Create function. For more information about these parameters, | ||||||
|  | // see the Volume object. | ||||||
|  | type CreateOpts struct { | ||||||
|  | 	// The size of the volume, in GB | ||||||
|  | 	Size int `json:"size" required:"true"` | ||||||
|  | 	// The availability zone | ||||||
|  | 	AvailabilityZone string `json:"availability_zone,omitempty"` | ||||||
|  | 	// ConsistencyGroupID is the ID of a consistency group | ||||||
|  | 	ConsistencyGroupID string `json:"consistencygroup_id,omitempty"` | ||||||
|  | 	// The volume description | ||||||
|  | 	Description string `json:"description,omitempty"` | ||||||
|  | 	// One or more metadata key and value pairs to associate with the volume | ||||||
|  | 	Metadata map[string]string `json:"metadata,omitempty"` | ||||||
|  | 	// The volume name | ||||||
|  | 	Name string `json:"name,omitempty"` | ||||||
|  | 	// the ID of the existing volume snapshot | ||||||
|  | 	SnapshotID string `json:"snapshot_id,omitempty"` | ||||||
|  | 	// SourceReplica is a UUID of an existing volume to replicate with | ||||||
|  | 	SourceReplica string `json:"source_replica,omitempty"` | ||||||
|  | 	// the ID of the existing volume | ||||||
|  | 	SourceVolID string `json:"source_volid,omitempty"` | ||||||
|  | 	// The ID of the image from which you want to create the volume. | ||||||
|  | 	// Required to create a bootable volume. | ||||||
|  | 	ImageID string `json:"imageRef,omitempty"` | ||||||
|  | 	// The associated volume type | ||||||
|  | 	VolumeType string `json:"volume_type,omitempty"` | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // ToVolumeCreateMap assembles a request body based on the contents of a | ||||||
|  | // CreateOpts. | ||||||
|  | func (opts CreateOpts) ToVolumeCreateMap() (map[string]interface{}, error) { | ||||||
|  | 	return gophercloud.BuildRequestBody(opts, "volume") | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // Create will create a new Volume based on the values in CreateOpts. To extract | ||||||
|  | // the Volume object from the response, call the Extract method on the | ||||||
|  | // CreateResult. | ||||||
|  | func Create(client *gophercloud.ServiceClient, opts CreateOptsBuilder) (r CreateResult) { | ||||||
|  | 	b, err := opts.ToVolumeCreateMap() | ||||||
|  | 	if err != nil { | ||||||
|  | 		r.Err = err | ||||||
|  | 		return | ||||||
|  | 	} | ||||||
|  | 	_, r.Err = client.Post(createURL(client), b, &r.Body, &gophercloud.RequestOpts{ | ||||||
|  | 		OkCodes: []int{202}, | ||||||
|  | 	}) | ||||||
|  | 	return | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // Delete will delete the existing Volume with the provided ID. | ||||||
|  | func Delete(client *gophercloud.ServiceClient, id string) (r DeleteResult) { | ||||||
|  | 	_, r.Err = client.Delete(deleteURL(client, id), nil) | ||||||
|  | 	return | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // Get retrieves the Volume with the provided ID. To extract the Volume object | ||||||
|  | // from the response, call the Extract method on the GetResult. | ||||||
|  | func Get(client *gophercloud.ServiceClient, id string) (r GetResult) { | ||||||
|  | 	_, r.Err = client.Get(getURL(client, id), &r.Body, nil) | ||||||
|  | 	return | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // ListOptsBuilder allows extensions to add additional parameters to the List | ||||||
|  | // request. | ||||||
|  | type ListOptsBuilder interface { | ||||||
|  | 	ToVolumeListQuery() (string, error) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // ListOpts holds options for listing Volumes. It is passed to the volumes.List | ||||||
|  | // function. | ||||||
|  | type ListOpts struct { | ||||||
|  | 	// admin-only option. Set it to true to see all tenant volumes. | ||||||
|  | 	AllTenants bool `q:"all_tenants"` | ||||||
|  | 	// List only volumes that contain Metadata. | ||||||
|  | 	Metadata map[string]string `q:"metadata"` | ||||||
|  | 	// List only volumes that have Name as the display name. | ||||||
|  | 	Name string `q:"name"` | ||||||
|  | 	// List only volumes that have a status of Status. | ||||||
|  | 	Status string `q:"status"` | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // ToVolumeListQuery formats a ListOpts into a query string. | ||||||
|  | func (opts ListOpts) ToVolumeListQuery() (string, error) { | ||||||
|  | 	q, err := gophercloud.BuildQueryString(opts) | ||||||
|  | 	return q.String(), err | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // List returns Volumes optionally limited by the conditions provided in ListOpts. | ||||||
|  | func List(client *gophercloud.ServiceClient, opts ListOptsBuilder) pagination.Pager { | ||||||
|  | 	url := listURL(client) | ||||||
|  | 	if opts != nil { | ||||||
|  | 		query, err := opts.ToVolumeListQuery() | ||||||
|  | 		if err != nil { | ||||||
|  | 			return pagination.Pager{Err: err} | ||||||
|  | 		} | ||||||
|  | 		url += query | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	return pagination.NewPager(client, url, func(r pagination.PageResult) pagination.Page { | ||||||
|  | 		return VolumePage{pagination.SinglePageBase(r)} | ||||||
|  | 	}) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // UpdateOptsBuilder allows extensions to add additional parameters to the | ||||||
|  | // Update request. | ||||||
|  | type UpdateOptsBuilder interface { | ||||||
|  | 	ToVolumeUpdateMap() (map[string]interface{}, error) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // UpdateOpts contain options for updating an existing Volume. This object is passed | ||||||
|  | // to the volumes.Update function. For more information about the parameters, see | ||||||
|  | // the Volume object. | ||||||
|  | type UpdateOpts struct { | ||||||
|  | 	Name        string            `json:"name,omitempty"` | ||||||
|  | 	Description string            `json:"description,omitempty"` | ||||||
|  | 	Metadata    map[string]string `json:"metadata,omitempty"` | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // ToVolumeUpdateMap assembles a request body based on the contents of an | ||||||
|  | // UpdateOpts. | ||||||
|  | func (opts UpdateOpts) ToVolumeUpdateMap() (map[string]interface{}, error) { | ||||||
|  | 	return gophercloud.BuildRequestBody(opts, "volume") | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // Update will update the Volume with provided information. To extract the updated | ||||||
|  | // Volume from the response, call the Extract method on the UpdateResult. | ||||||
|  | func Update(client *gophercloud.ServiceClient, id string, opts UpdateOptsBuilder) (r UpdateResult) { | ||||||
|  | 	b, err := opts.ToVolumeUpdateMap() | ||||||
|  | 	if err != nil { | ||||||
|  | 		r.Err = err | ||||||
|  | 		return | ||||||
|  | 	} | ||||||
|  | 	_, r.Err = client.Put(updateURL(client, id), b, &r.Body, &gophercloud.RequestOpts{ | ||||||
|  | 		OkCodes: []int{200}, | ||||||
|  | 	}) | ||||||
|  | 	return | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // IDFromName is a convienience function that returns a server's ID given its name. | ||||||
|  | func IDFromName(client *gophercloud.ServiceClient, name string) (string, error) { | ||||||
|  | 	count := 0 | ||||||
|  | 	id := "" | ||||||
|  | 	pages, err := List(client, nil).AllPages() | ||||||
|  | 	if err != nil { | ||||||
|  | 		return "", err | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	all, err := ExtractVolumes(pages) | ||||||
|  | 	if err != nil { | ||||||
|  | 		return "", err | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	for _, s := range all { | ||||||
|  | 		if s.Name == name { | ||||||
|  | 			count++ | ||||||
|  | 			id = s.ID | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	switch count { | ||||||
|  | 	case 0: | ||||||
|  | 		return "", gophercloud.ErrResourceNotFound{Name: name, ResourceType: "volume"} | ||||||
|  | 	case 1: | ||||||
|  | 		return id, nil | ||||||
|  | 	default: | ||||||
|  | 		return "", gophercloud.ErrMultipleResourcesFound{Name: name, Count: count, ResourceType: "volume"} | ||||||
|  | 	} | ||||||
|  | } | ||||||
							
								
								
									
										121
									
								
								vendor/github.com/gophercloud/gophercloud/openstack/blockstorage/v2/volumes/results.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										121
									
								
								vendor/github.com/gophercloud/gophercloud/openstack/blockstorage/v2/volumes/results.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,121 @@ | |||||||
|  | package volumes | ||||||
|  |  | ||||||
|  | import ( | ||||||
|  | 	"github.com/gophercloud/gophercloud" | ||||||
|  | 	"github.com/gophercloud/gophercloud/pagination" | ||||||
|  | ) | ||||||
|  |  | ||||||
|  | type Attachment struct { | ||||||
|  | 	AttachedAt   gophercloud.JSONRFC3339MilliNoZ `json:"attached_at"` | ||||||
|  | 	AttachmentID string                          `json:"attachment_id"` | ||||||
|  | 	Device       string                          `json:"device"` | ||||||
|  | 	HostName     string                          `json:"host_name"` | ||||||
|  | 	ID           string                          `json:"id"` | ||||||
|  | 	ServerID     string                          `json:"server_id"` | ||||||
|  | 	VolumeID     string                          `json:"volume_id"` | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // Volume contains all the information associated with an OpenStack Volume. | ||||||
|  | type Volume struct { | ||||||
|  | 	// Unique identifier for the volume. | ||||||
|  | 	ID string `json:"id"` | ||||||
|  | 	// Current status of the volume. | ||||||
|  | 	Status string `json:"status"` | ||||||
|  | 	// Size of the volume in GB. | ||||||
|  | 	Size int `json:"size"` | ||||||
|  | 	// AvailabilityZone is which availability zone the volume is in. | ||||||
|  | 	AvailabilityZone string `json:"availability_zone"` | ||||||
|  | 	// The date when this volume was created. | ||||||
|  | 	CreatedAt gophercloud.JSONRFC3339MilliNoZ `json:"created_at"` | ||||||
|  | 	// The date when this volume was last updated | ||||||
|  | 	UpdatedAt gophercloud.JSONRFC3339MilliNoZ `json:"updated_at"` | ||||||
|  | 	// Instances onto which the volume is attached. | ||||||
|  | 	Attachments []Attachment `json:"attachments"` | ||||||
|  | 	// Human-readable display name for the volume. | ||||||
|  | 	Name string `json:"name"` | ||||||
|  | 	// Human-readable description for the volume. | ||||||
|  | 	Description string `json:"description"` | ||||||
|  | 	// The type of volume to create, either SATA or SSD. | ||||||
|  | 	VolumeType string `json:"volume_type"` | ||||||
|  | 	// The ID of the snapshot from which the volume was created | ||||||
|  | 	SnapshotID string `json:"snapshot_id"` | ||||||
|  | 	// The ID of another block storage volume from which the current volume was created | ||||||
|  | 	SourceVolID string `json:"source_volid"` | ||||||
|  | 	// Arbitrary key-value pairs defined by the user. | ||||||
|  | 	Metadata map[string]string `json:"metadata"` | ||||||
|  | 	// UserID is the id of the user who created the volume. | ||||||
|  | 	UserID string `json:"user_id"` | ||||||
|  | 	// Indicates whether this is a bootable volume. | ||||||
|  | 	Bootable string `json:"bootable"` | ||||||
|  | 	// Encrypted denotes if the volume is encrypted. | ||||||
|  | 	Encrypted bool `json:"encrypted"` | ||||||
|  | 	// ReplicationStatus is the status of replication. | ||||||
|  | 	ReplicationStatus string `json:"replication_status"` | ||||||
|  | 	// ConsistencyGroupID is the consistency group ID. | ||||||
|  | 	ConsistencyGroupID string `json:"consistencygroup_id"` | ||||||
|  | 	// Multiattach denotes if the volume is multi-attach capable. | ||||||
|  | 	Multiattach bool `json:"multiattach"` | ||||||
|  | } | ||||||
|  |  | ||||||
|  | /* | ||||||
|  | THESE BELONG IN EXTENSIONS: | ||||||
|  | // ReplicationDriverData contains data about the replication driver. | ||||||
|  | ReplicationDriverData string `json:"os-volume-replication:driver_data"` | ||||||
|  | // ReplicationExtendedStatus contains extended status about replication. | ||||||
|  | ReplicationExtendedStatus string `json:"os-volume-replication:extended_status"` | ||||||
|  | // TenantID is the id of the project that owns the volume. | ||||||
|  | TenantID string `json:"os-vol-tenant-attr:tenant_id"` | ||||||
|  | */ | ||||||
|  |  | ||||||
|  | // VolumePage is a pagination.pager that is returned from a call to the List function. | ||||||
|  | type VolumePage struct { | ||||||
|  | 	pagination.SinglePageBase | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // IsEmpty returns true if a ListResult contains no Volumes. | ||||||
|  | func (r VolumePage) IsEmpty() (bool, error) { | ||||||
|  | 	volumes, err := ExtractVolumes(r) | ||||||
|  | 	return len(volumes) == 0, err | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // ExtractVolumes extracts and returns Volumes. It is used while iterating over a volumes.List call. | ||||||
|  | func ExtractVolumes(r pagination.Page) ([]Volume, error) { | ||||||
|  | 	var s struct { | ||||||
|  | 		Volumes []Volume `json:"volumes"` | ||||||
|  | 	} | ||||||
|  | 	err := (r.(VolumePage)).ExtractInto(&s) | ||||||
|  | 	return s.Volumes, err | ||||||
|  | } | ||||||
|  |  | ||||||
|  | type commonResult struct { | ||||||
|  | 	gophercloud.Result | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // Extract will get the Volume object out of the commonResult object. | ||||||
|  | func (r commonResult) Extract() (*Volume, error) { | ||||||
|  | 	var s struct { | ||||||
|  | 		Volume *Volume `json:"volume"` | ||||||
|  | 	} | ||||||
|  | 	err := r.ExtractInto(&s) | ||||||
|  | 	return s.Volume, err | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // CreateResult contains the response body and error from a Create request. | ||||||
|  | type CreateResult struct { | ||||||
|  | 	commonResult | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // GetResult contains the response body and error from a Get request. | ||||||
|  | type GetResult struct { | ||||||
|  | 	commonResult | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // UpdateResult contains the response body and error from an Update request. | ||||||
|  | type UpdateResult struct { | ||||||
|  | 	commonResult | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // DeleteResult contains the response body and error from a Delete request. | ||||||
|  | type DeleteResult struct { | ||||||
|  | 	gophercloud.ErrResult | ||||||
|  | } | ||||||
							
								
								
									
										23
									
								
								vendor/github.com/gophercloud/gophercloud/openstack/blockstorage/v2/volumes/urls.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										23
									
								
								vendor/github.com/gophercloud/gophercloud/openstack/blockstorage/v2/volumes/urls.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,23 @@ | |||||||
|  | package volumes | ||||||
|  |  | ||||||
|  | import "github.com/gophercloud/gophercloud" | ||||||
|  |  | ||||||
|  | func createURL(c *gophercloud.ServiceClient) string { | ||||||
|  | 	return c.ServiceURL("volumes") | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func listURL(c *gophercloud.ServiceClient) string { | ||||||
|  | 	return c.ServiceURL("volumes", "detail") | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func deleteURL(c *gophercloud.ServiceClient, id string) string { | ||||||
|  | 	return c.ServiceURL("volumes", id) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func getURL(c *gophercloud.ServiceClient, id string) string { | ||||||
|  | 	return deleteURL(c, id) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func updateURL(c *gophercloud.ServiceClient, id string) string { | ||||||
|  | 	return deleteURL(c, id) | ||||||
|  | } | ||||||
							
								
								
									
										22
									
								
								vendor/github.com/gophercloud/gophercloud/openstack/blockstorage/v2/volumes/util.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										22
									
								
								vendor/github.com/gophercloud/gophercloud/openstack/blockstorage/v2/volumes/util.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,22 @@ | |||||||
|  | package volumes | ||||||
|  |  | ||||||
|  | import ( | ||||||
|  | 	"github.com/gophercloud/gophercloud" | ||||||
|  | ) | ||||||
|  |  | ||||||
|  | // WaitForStatus will continually poll the resource, checking for a particular | ||||||
|  | // status. It will do this for the amount of seconds defined. | ||||||
|  | func WaitForStatus(c *gophercloud.ServiceClient, id, status string, secs int) error { | ||||||
|  | 	return gophercloud.WaitFor(secs, func() (bool, error) { | ||||||
|  | 		current, err := Get(c, id).Extract() | ||||||
|  | 		if err != nil { | ||||||
|  | 			return false, err | ||||||
|  | 		} | ||||||
|  |  | ||||||
|  | 		if current.Status == status { | ||||||
|  | 			return true, nil | ||||||
|  | 		} | ||||||
|  |  | ||||||
|  | 		return false, nil | ||||||
|  | 	}) | ||||||
|  | } | ||||||
		Reference in New Issue
	
	Block a user
	 Kubernetes Submit Queue
					Kubernetes Submit Queue