deltacloud-core 0.5.0 → 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (294) hide show
  1. data/NOTICE +1 -1
  2. data/Rakefile +63 -21
  3. data/bin/deltacloudd +10 -6
  4. data/config.ru +62 -6
  5. data/config/drivers/ec2.yaml +8 -0
  6. data/config/drivers/fgcp.yaml +13 -0
  7. data/deltacloud-core.gemspec +10 -2
  8. data/lib/cimi/collections.rb +58 -0
  9. data/lib/cimi/collections/address_templates.rb +49 -0
  10. data/lib/cimi/collections/addresses.rb +74 -0
  11. data/lib/cimi/collections/cloud_entry_point.rb +37 -0
  12. data/lib/{deltacloud/helpers/conversion_helper.rb → cimi/collections/entity_metadata.rb} +24 -16
  13. data/lib/cimi/collections/machine_admins.rb +74 -0
  14. data/lib/cimi/collections/machine_configurations.rb +49 -0
  15. data/lib/cimi/collections/machine_images.rb +50 -0
  16. data/lib/cimi/collections/machines.rb +157 -0
  17. data/lib/cimi/collections/network_configurations.rb +47 -0
  18. data/lib/cimi/collections/network_templates.rb +48 -0
  19. data/lib/cimi/collections/networks.rb +125 -0
  20. data/lib/cimi/collections/routing_group_templates.rb +47 -0
  21. data/lib/cimi/collections/routing_groups.rb +48 -0
  22. data/lib/cimi/collections/volume_configurations.rb +48 -0
  23. data/lib/cimi/collections/volume_images.rb +50 -0
  24. data/lib/cimi/collections/volumes.rb +80 -0
  25. data/lib/cimi/collections/vsp_configurations.rb +48 -0
  26. data/lib/cimi/collections/vsp_templates.rb +50 -0
  27. data/lib/cimi/collections/vsps.rb +108 -0
  28. data/lib/cimi/dependencies.rb +1 -0
  29. data/lib/cimi/helpers.rb +116 -0
  30. data/lib/cimi/helpers/cimi_helper.rb +6 -2
  31. data/lib/cimi/models.rb +72 -0
  32. data/lib/cimi/{model → models}/action.rb +1 -1
  33. data/lib/cimi/models/address.rb +72 -0
  34. data/lib/cimi/models/address_collection.rb +34 -0
  35. data/lib/cimi/models/address_template.rb +54 -0
  36. data/lib/cimi/models/address_template_collection.rb +34 -0
  37. data/lib/cimi/{model → models}/base.rb +2 -2
  38. data/lib/cimi/{model → models}/cloud_entry_point.rb +3 -5
  39. data/lib/cimi/{model → models}/entity_metadata.rb +7 -6
  40. data/lib/cimi/{model → models}/entity_metadata_collection.rb +2 -2
  41. data/lib/cimi/{model → models}/errors.rb +8 -0
  42. data/lib/cimi/{model → models}/machine.rb +10 -10
  43. data/lib/cimi/{model → models}/machine_admin.rb +1 -1
  44. data/lib/cimi/{model → models}/machine_admin_collection.rb +2 -2
  45. data/lib/cimi/{model → models}/machine_collection.rb +2 -2
  46. data/lib/cimi/{model → models}/machine_configuration.rb +9 -6
  47. data/lib/cimi/{model → models}/machine_configuration_collection.rb +2 -2
  48. data/lib/cimi/{model → models}/machine_image.rb +1 -1
  49. data/lib/cimi/{model → models}/machine_image_collection.rb +2 -2
  50. data/lib/cimi/{model → models}/machine_template.rb +0 -0
  51. data/lib/cimi/{model → models}/machine_template_collection.rb +2 -2
  52. data/lib/cimi/models/network.rb +109 -0
  53. data/lib/cimi/{model → models}/network_collection.rb +2 -2
  54. data/lib/cimi/{model → models}/network_configuration.rb +3 -4
  55. data/lib/cimi/{model → models}/network_configuration_collection.rb +2 -2
  56. data/lib/cimi/models/network_template.rb +36 -0
  57. data/lib/cimi/models/network_template_collection.rb +35 -0
  58. data/lib/cimi/models/routing_group.rb +34 -0
  59. data/lib/cimi/models/routing_group_collection.rb +34 -0
  60. data/lib/cimi/models/routing_group_template.rb +34 -0
  61. data/lib/cimi/models/routing_group_template_collection.rb +35 -0
  62. data/lib/cimi/{model → models}/schema.rb +0 -0
  63. data/lib/cimi/{model → models}/volume.rb +4 -4
  64. data/lib/cimi/{model → models}/volume_collection.rb +2 -2
  65. data/lib/cimi/{model → models}/volume_configuration.rb +1 -1
  66. data/lib/cimi/{model → models}/volume_configuration_collection.rb +2 -2
  67. data/lib/cimi/{model → models}/volume_image.rb +1 -1
  68. data/lib/cimi/{model → models}/volume_image_collection.rb +2 -2
  69. data/lib/cimi/{model → models}/volume_template.rb +0 -0
  70. data/lib/cimi/{model → models}/volume_template_collection.rb +2 -2
  71. data/lib/cimi/models/vsp.rb +102 -0
  72. data/lib/cimi/models/vsp_collection.rb +34 -0
  73. data/lib/cimi/models/vsp_configuration.rb +40 -0
  74. data/lib/cimi/models/vsp_configuration_collection.rb +34 -0
  75. data/lib/cimi/models/vsp_template.rb +34 -0
  76. data/lib/cimi/models/vsp_template_collection.rb +34 -0
  77. data/lib/cimi/server.rb +27 -549
  78. data/lib/deltacloud/api.rb +79 -0
  79. data/lib/deltacloud/collections.rb +54 -0
  80. data/lib/deltacloud/collections/addresses.rb +91 -0
  81. data/lib/deltacloud/collections/buckets.rb +273 -0
  82. data/lib/deltacloud/collections/drivers.rb +51 -0
  83. data/lib/deltacloud/collections/firewalls.rb +116 -0
  84. data/lib/deltacloud/collections/hardware_profiles.rb +29 -0
  85. data/lib/deltacloud/collections/images.rb +73 -0
  86. data/lib/deltacloud/collections/instance_states.rb +59 -0
  87. data/lib/deltacloud/collections/instances.rb +113 -0
  88. data/lib/deltacloud/collections/keys.rb +61 -0
  89. data/lib/deltacloud/collections/load_balancers.rb +102 -0
  90. data/lib/deltacloud/collections/metrics.rb +28 -0
  91. data/lib/deltacloud/collections/realms.rb +28 -0
  92. data/lib/deltacloud/collections/storage_snapshots.rb +51 -0
  93. data/lib/deltacloud/collections/storage_volumes.rb +99 -0
  94. data/lib/deltacloud/core_ext.rb +14 -6
  95. data/lib/deltacloud/core_ext/string.rb +17 -5
  96. data/lib/deltacloud/drivers.rb +6 -42
  97. data/lib/deltacloud/drivers/azure/azure_driver.rb +0 -4
  98. data/lib/deltacloud/{base_driver → drivers}/base_driver.rb +56 -18
  99. data/lib/deltacloud/drivers/condor/condor_driver.rb +1 -8
  100. data/lib/deltacloud/drivers/ec2/ec2_driver.rb +142 -33
  101. data/lib/deltacloud/drivers/eucalyptus/eucalyptus_driver.rb +2 -6
  102. data/lib/deltacloud/{base_driver → drivers}/exceptions.rb +25 -2
  103. data/lib/deltacloud/drivers/features.rb +154 -0
  104. data/lib/deltacloud/drivers/fgcp/fgcp_client.rb +387 -0
  105. data/lib/deltacloud/drivers/fgcp/fgcp_driver.rb +1435 -0
  106. data/lib/deltacloud/drivers/gogrid/gogrid_driver.rb +8 -11
  107. data/lib/deltacloud/drivers/google/google_driver.rb +2 -5
  108. data/lib/deltacloud/drivers/mock/data/keys/test-key.yml +28 -0
  109. data/lib/deltacloud/drivers/mock/mock_client.rb +2 -2
  110. data/lib/deltacloud/drivers/mock/mock_driver.rb +58 -40
  111. data/lib/deltacloud/drivers/mock/mock_driver_cimi_methods.rb +145 -0
  112. data/lib/deltacloud/drivers/opennebula/cloud_client.rb +107 -73
  113. data/lib/deltacloud/drivers/opennebula/occi_client.rb +285 -145
  114. data/lib/deltacloud/drivers/opennebula/opennebula_driver.rb +189 -126
  115. data/lib/deltacloud/drivers/openstack/openstack_driver.rb +427 -8
  116. data/lib/deltacloud/drivers/rackspace/rackspace_driver.rb +7 -9
  117. data/lib/deltacloud/drivers/rhevm/rhevm_driver.rb +48 -66
  118. data/lib/deltacloud/drivers/rimuhosting/rimuhosting_client.rb +44 -51
  119. data/lib/deltacloud/drivers/rimuhosting/rimuhosting_driver.rb +7 -8
  120. data/lib/deltacloud/drivers/sbc/sbc_client.rb +1 -1
  121. data/lib/deltacloud/drivers/sbc/sbc_driver.rb +3 -3
  122. data/lib/deltacloud/drivers/terremark/terremark_driver.rb +8 -8
  123. data/lib/deltacloud/drivers/vsphere/vsphere_driver.rb +39 -13
  124. data/lib/deltacloud/drivers/vsphere/vsphere_filemanager.rb +1 -0
  125. data/lib/deltacloud/helpers.rb +79 -7
  126. data/lib/{sinatra/lazy_auth.rb → deltacloud/helpers/auth_helper.rb} +5 -9
  127. data/lib/deltacloud/helpers/{blob_stream.rb → blob_stream_helper.rb} +7 -7
  128. data/lib/deltacloud/helpers/deltacloud_helper.rb +286 -0
  129. data/lib/deltacloud/helpers/driver_helper.rb +58 -0
  130. data/lib/deltacloud/helpers/rabbit_helper.rb +34 -0
  131. data/lib/{sinatra/url_for.rb → deltacloud/helpers/url_helper.rb} +31 -9
  132. data/lib/deltacloud/models.rb +19 -17
  133. data/lib/deltacloud/models/bucket.rb +4 -0
  134. data/lib/deltacloud/{hardware_profile.rb → models/hardware_profile.rb} +4 -2
  135. data/lib/deltacloud/models/image.rb +1 -0
  136. data/lib/deltacloud/models/instance.rb +4 -2
  137. data/lib/deltacloud/models/instance_address.rb +4 -0
  138. data/lib/deltacloud/models/key.rb +4 -0
  139. data/lib/deltacloud/models/metric.rb +40 -0
  140. data/lib/deltacloud/{state_machine.rb → models/state_machine.rb} +18 -1
  141. data/lib/deltacloud/server.rb +41 -1222
  142. data/lib/deltacloud_rack.rb +79 -0
  143. data/lib/{cimi/model/network_template.rb → ec2/helpers.rb} +7 -10
  144. data/lib/{deltacloud/backend_capability.rb → ec2/helpers/errors.rb} +24 -29
  145. data/lib/ec2/helpers/result.rb +31 -0
  146. data/lib/ec2/query_parser.rb +152 -0
  147. data/lib/ec2/server.rb +70 -0
  148. data/lib/{deltacloud.rb → sinatra.rb} +7 -12
  149. data/lib/sinatra/rack_accept.rb +13 -7
  150. data/lib/sinatra/rack_driver_select.rb +1 -1
  151. data/lib/sinatra/rack_matrix_params.rb +1 -3
  152. data/public/images/metric.png +0 -0
  153. data/public/javascripts/jquery.mobile-1.0.1.min.js +177 -0
  154. data/public/stylesheets/jquery.mobile-1.0.1.min.css +2 -0
  155. data/public/stylesheets/new.css +10 -0
  156. data/support/fedora/deltacloud-core.spec +1 -1
  157. data/tests/api/common.rb +1 -0
  158. data/tests/api/driver_test.rb +79 -0
  159. data/tests/api/library_test.rb +48 -0
  160. data/tests/cimi/features/step_definitions/common_steps.rb +2 -2
  161. data/tests/cimi/features/step_definitions/machines_steps.rb +5 -4
  162. data/tests/cimi/features/step_definitions/volumes_steps.rb +8 -8
  163. data/tests/cimi/features/support/env.rb +33 -11
  164. data/tests/common.rb +7 -8
  165. data/tests/drivers/ec2/api_test.rb +19 -0
  166. data/tests/drivers/ec2/common.rb +23 -0
  167. data/tests/drivers/ec2/drivers_test.rb +120 -0
  168. data/tests/drivers/ec2/hardware_profiles_test.rb +224 -0
  169. data/tests/drivers/ec2/images_test.rb +230 -0
  170. data/tests/drivers/ec2/instances_test.rb +356 -0
  171. data/tests/drivers/ec2/keys_test.rb +181 -0
  172. data/tests/drivers/ec2/realms_test.rb +146 -0
  173. data/tests/drivers/fgcp/api_test.rb +47 -0
  174. data/tests/drivers/fgcp/hardware_profiles_test.rb +54 -0
  175. data/tests/drivers/fgcp/realms_test.rb +42 -0
  176. data/tests/drivers/{rackspace → fgcp}/setup.rb +5 -6
  177. data/tests/drivers/google/api_test.rb +10 -26
  178. data/tests/drivers/google/buckets_test.rb +79 -95
  179. data/tests/drivers/google/common.rb +54 -0
  180. data/tests/drivers/mock/api_test.rb +4 -127
  181. data/tests/drivers/mock/buckets_test.rb +195 -0
  182. data/tests/drivers/mock/common.rb +7 -0
  183. data/tests/drivers/mock/drivers_test.rb +123 -0
  184. data/tests/drivers/mock/hardware_profiles_test.rb +190 -100
  185. data/tests/drivers/mock/images_test.rb +162 -103
  186. data/tests/drivers/mock/instances_test.rb +310 -220
  187. data/tests/drivers/mock/keys_test.rb +161 -0
  188. data/tests/drivers/mock/realms_test.rb +109 -70
  189. data/tests/drivers/mock/storage_snapshots_test.rb +114 -0
  190. data/tests/drivers/mock/storage_volumes_test.rb +122 -0
  191. data/tests/drivers/openstack/api_test.rb +8 -3
  192. data/tests/drivers/openstack/common.rb +21 -0
  193. data/tests/drivers/openstack/hardware_profiles_test.rb +20 -9
  194. data/tests/drivers/openstack/images_test.rb +11 -5
  195. data/tests/drivers/openstack/instances_test.rb +61 -16
  196. data/tests/drivers/openstack/realms_test.rb +11 -7
  197. data/tests/drivers/rackspace/api_test.rb +7 -2
  198. data/tests/drivers/rackspace/buckets_test.rb +7 -2
  199. data/tests/drivers/rackspace/common.rb +16 -0
  200. data/tests/drivers/rackspace/hardware_profiles_test.rb +7 -2
  201. data/tests/drivers/rackspace/images_test.rb +7 -2
  202. data/tests/drivers/rackspace/instances_test.rb +10 -5
  203. data/tests/drivers/rackspace/realms_test.rb +7 -2
  204. data/tests/drivers/rhevm/api_test.rb +12 -6
  205. data/tests/drivers/rhevm/{setup.rb → common.rb} +8 -1
  206. data/tests/drivers/rhevm/hardware_profiles_test.rb +7 -2
  207. data/tests/drivers/rhevm/images_test.rb +8 -2
  208. data/tests/drivers/rhevm/instances_test.rb +7 -2
  209. data/tests/drivers/rhevm/realms_test.rb +7 -2
  210. data/tests/minitest_common.rb +58 -0
  211. data/tests/minitest_common_api_test.rb +115 -0
  212. data/views/addresses/associate.html.haml +1 -1
  213. data/views/addresses/index.html.haml +1 -1
  214. data/views/addresses/show.html.haml +2 -3
  215. data/views/api/show.html.haml +22 -9
  216. data/views/api/show.xml.haml +8 -7
  217. data/views/blobs/show.xml.haml +7 -4
  218. data/views/buckets/new.html.haml +2 -2
  219. data/views/cimi/errors/401.xml.haml +1 -1
  220. data/views/error.html.haml +3 -3
  221. data/views/errors/400.html.haml +1 -1
  222. data/views/errors/400.xml.haml +1 -2
  223. data/views/errors/401.html.haml +8 -7
  224. data/views/errors/401.xml.haml +1 -1
  225. data/views/errors/404.xml.haml +1 -0
  226. data/views/errors/500.xml.haml +4 -2
  227. data/views/{cimi/errors/403.html.haml → errors/501.html.haml} +4 -2
  228. data/views/errors/501.xml.haml +1 -0
  229. data/views/errors/502.xml.haml +1 -7
  230. data/views/{cimi/errors/500.html.haml → errors/504.html.haml} +0 -0
  231. data/views/errors/504.xml.haml +1 -0
  232. data/views/firewalls/new.html.haml +2 -2
  233. data/views/firewalls/new_rule.html.haml +1 -1
  234. data/views/firewalls/show.html.haml +1 -1
  235. data/views/hardware_profiles/index.html.haml +3 -1
  236. data/views/hardware_profiles/index.xml.haml +2 -2
  237. data/views/hardware_profiles/show.html.haml +3 -3
  238. data/views/hardware_profiles/show.xml.haml +3 -3
  239. data/views/images/show.html.haml +5 -0
  240. data/views/images/show.xml.haml +6 -1
  241. data/views/instance_states/show.html.haml +1 -1
  242. data/views/instances/index.html.haml +1 -1
  243. data/views/instances/new.html.haml +54 -39
  244. data/views/instances/run_command.html.haml +24 -15
  245. data/views/instances/show.html.haml +7 -3
  246. data/views/instances/show.xml.haml +2 -2
  247. data/views/keys/show.xml.haml +1 -0
  248. data/views/layout.html.haml +5 -9
  249. data/views/load_balancers/show.html.haml +12 -6
  250. data/views/metrics/index.html.haml +13 -0
  251. data/views/metrics/index.xml.haml +5 -0
  252. data/views/metrics/show.html.haml +23 -0
  253. data/views/metrics/show.xml.haml +9 -0
  254. data/views/realms/show.xml.haml +2 -1
  255. data/views/root/index.html.haml +1 -1
  256. data/views/storage_snapshots/show.html.haml +1 -1
  257. data/views/storage_snapshots/show.xml.haml +1 -0
  258. data/views/storage_volumes/attach.html.haml +1 -2
  259. data/views/storage_volumes/index.html.haml +1 -1
  260. data/views/storage_volumes/new.html.haml +22 -16
  261. data/views/storage_volumes/show.html.haml +10 -4
  262. data/views/storage_volumes/show.xml.haml +3 -4
  263. metadata +547 -519
  264. data/DISCLAIMER +0 -8
  265. data/lib/cimi/model.rb +0 -52
  266. data/lib/cimi/model/network.rb +0 -69
  267. data/lib/deltacloud/base_driver.rb +0 -18
  268. data/lib/deltacloud/base_driver/features.rb +0 -262
  269. data/lib/deltacloud/base_driver/mock_driver.rb +0 -78
  270. data/lib/deltacloud/drivers/ec2/ec2_mock_driver.rb +0 -186
  271. data/lib/deltacloud/drivers/rhevm/rhevm_client.rb +0 -521
  272. data/lib/deltacloud/helpers/application_helper.rb +0 -267
  273. data/lib/deltacloud/helpers/hardware_profiles_helper.rb +0 -50
  274. data/lib/deltacloud/helpers/json_helper.rb +0 -31
  275. data/lib/deltacloud/method_serializer.rb +0 -83
  276. data/lib/deltacloud/validation.rb +0 -107
  277. data/lib/sinatra/rabbit.rb +0 -441
  278. data/lib/sinatra/rack_runtime.rb +0 -47
  279. data/lib/sinatra/rack_syslog.rb +0 -86
  280. data/lib/sinatra/sinatra_verbose.rb +0 -73
  281. data/lib/sinatra/static_assets.rb +0 -99
  282. data/public/javascripts/jquery.mobile-1.0rc1.min.js +0 -170
  283. data/public/stylesheets/jquery.mobile-1.0rc1.min.css +0 -12
  284. data/tests/drivers/google/setup.rb +0 -38
  285. data/tests/drivers/mock/instance_states_test.rb +0 -71
  286. data/tests/drivers/mock/setup.rb +0 -3
  287. data/tests/drivers/mock/url_for_test.rb +0 -67
  288. data/tests/drivers/openstack/setup.rb +0 -20
  289. data/views/cimi/errors/400.html.haml +0 -41
  290. data/views/cimi/errors/401.html.haml +0 -41
  291. data/views/cimi/errors/404.html.haml +0 -29
  292. data/views/cimi/errors/405.html.haml +0 -29
  293. data/views/cimi/errors/502.html.haml +0 -43
  294. data/views/cimi/errors/backend_capability_failure.html.haml +0 -29
@@ -0,0 +1,1435 @@
1
+ # Licensed to the Apache Software Foundation (ASF) under one or more
2
+ # contributor license agreements. See the NOTICE file distributed with
3
+ # this work for additional information regarding copyright ownership. The
4
+ # ASF licenses this file to you under the Apache License, Version 2.0 (the
5
+ # "License"); you may not use this file except in compliance with the
6
+ # License. You may obtain a copy of the License at
7
+ #
8
+ # http://www.apache.org/licenses/LICENSE-2.0
9
+ #
10
+ # Unless required by applicable law or agreed to in writing, software
11
+ # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
12
+ # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
13
+ # License for the specific language governing permissions and limitations
14
+ # under the License.
15
+ #
16
+ # Author: Dies Koper <diesk@fast.au.fujitsu.com>
17
+
18
+ require_relative 'fgcp_client'
19
+ require_relative '../../runner'
20
+ require 'openssl'
21
+ require 'xmlsimple'
22
+
23
+ module Deltacloud
24
+ module Drivers
25
+ module Fgcp
26
+ class FgcpDriver < Deltacloud::BaseDriver
27
+
28
+ CERT_DIR = ENV['FGCP_CERT_DIR'] || File::expand_path('~/.deltacloud/config/fgcp')
29
+
30
+ def supported_collections
31
+ DEFAULT_COLLECTIONS + [ :addresses, :load_balancers, :firewalls ]
32
+ end
33
+
34
+ feature :instances, :user_name
35
+ feature :images, :user_name
36
+ feature :images, :user_description
37
+
38
+ define_hardware_profile('default')
39
+
40
+ def valid_credentials?(credentials)
41
+ begin
42
+ client = new_client(credentials)
43
+ # use a relativily cheap operation that is likely to succeed
44
+ # (i.e. not requiring particular access privileges)
45
+ client.list_server_types
46
+ rescue
47
+ return false
48
+ end
49
+ true
50
+ end
51
+
52
+ ######################################################################
53
+ # Instance state machine
54
+ #####################################################################
55
+ define_instance_states do
56
+ start.to( :pending ) .on( :create ) # new instances do not start automatically
57
+ pending.to( :stopped ) .automatically # after creation they are in a stopped state
58
+ # running.to( :running ) .on( :reboot ) # there is no single reboot operation
59
+ running.to( :stopping ) .on( :stop ) # stopping an instance does not automatically destroy it
60
+ # running.to( :finish ) .on( :destroy ) # running instances cannot be destroyed in a single step; they need to be stopped first
61
+ stopping.to( :stopped ) .automatically # stopping an instance does not automatically destroy it
62
+ stopped.to(:running) .on( :start ) # obvious
63
+ stopped.to(:finish) .on( :destroy ) # only destroy removes an instance, and it has to be stopped first
64
+ end
65
+
66
+ ######################################################################
67
+ # Hardware profiles
68
+ #####################################################################
69
+ def hardware_profiles(credentials, opts=nil)
70
+ safely do
71
+ client = new_client(credentials)
72
+ xml = client.list_server_types
73
+
74
+ @hardware_profiles = []
75
+ if xml['servertypes']
76
+ xml['servertypes'][0]['servertype'].each do |type|
77
+
78
+ arch = type['cpu'][0]['cpuArch'][0] # returns 'IA' or 'SPARC'. IA currently offered is x86_64
79
+ cpu = type['cpu'][0]['cpuPerf'][0].to_f * type['cpu'][0]['numOfCpu'][0].to_i
80
+
81
+ @hardware_profiles << ::Deltacloud::HardwareProfile.new(type['name'][0]) {
82
+ cpu cpu.to_f == cpu.to_f.floor ? cpu.to_i : cpu.to_f # omit '.0' if whole number
83
+ memory (type['memory'][0]['memorySize'][0].to_f * 1024) # converted to MB
84
+ architecture (arch == 'IA') ? 'x86_64' : arch
85
+ #storage <- defined by image, not hardware profile
86
+ #if 'storage' is not added, displays 'storage:0' in GUI
87
+ #storage ''
88
+ }
89
+ end
90
+ end
91
+ end
92
+ #TODO: is there a way to sort them by arch and memory?
93
+ #current order is as returned by API which is not a natural order
94
+ # @hardware_profiles.sort_by{|e| [e.architecture, e.memory]}
95
+ filter_hardware_profiles(@hardware_profiles, opts)
96
+ end
97
+
98
+ ######################################################################
99
+ # Images
100
+ ######################################################################
101
+ def images(credentials, opts={})
102
+ images = []
103
+
104
+ safely do
105
+ client = new_client(credentials)
106
+ xml = client.list_disk_images
107
+ hwps = hardware_profiles(credentials)
108
+
109
+ # use client to get a list of images from the back-end cloud and then create
110
+ # a Deltacloud Image object for each of these. Filter the result
111
+ # (eg specific image requested) and return to user
112
+ if xml['diskimages'] # not likely to not be so, but just in case
113
+ xml['diskimages'][0]['diskimage'].each do |img|
114
+
115
+ images << Image.new(
116
+ :id => img['diskimageId'][0],
117
+ :name => img['diskimageName'][0].to_s,
118
+ :description => img['description'][0].to_s,
119
+ :owner_id => img['registrant'][0].to_s, # or 'creatorName'?
120
+ :state => 'AVAILABLE', #server keeps no particular state. If it's listed, it's available for use.
121
+ # This will determine image architecture using OS name.
122
+ # Usually the OS name includes '64bit' or '32bit'. If not,
123
+ # it will fall back to 64 bit.
124
+ :architecture => img['osName'][0].to_s =~ /.*32.?bit.*/ ? 'i386' : 'x86_64',
125
+ :hardware_profiles => hwps
126
+ ) if opts[:id].nil? or opts[:id] == img['diskimageId'][0]
127
+ end
128
+ end
129
+ end
130
+
131
+ images = filter_on( images, :id, opts )
132
+ images = filter_on( images, :architecture, opts )
133
+ images = filter_on( images, :owner_id, opts )
134
+ images.sort_by{|e| [e.owner_id, e.architecture, e.name, e.description]}
135
+ end
136
+
137
+ # Create a new image from the given instance, with optionally provided name and description
138
+ def create_image(credentials, opts={})
139
+ safely do
140
+ client = new_client(credentials)
141
+
142
+ if opts[:name].nil?
143
+ # default to instance name
144
+ instance = client.get_vserver_attributes(opts[:id])
145
+ opts[:name] ||= instance['vserver'][0]['vserverName']
146
+ opts[:description] ||= opts[:name]
147
+ end
148
+
149
+ client.register_private_disk_image(opts[:id], opts[:name], opts[:description])
150
+ hwps = hardware_profiles(credentials)
151
+
152
+ #can't retrieve image info until it's completed
153
+ Image.new(
154
+ :id => "PENDING-#{opts[:name]}", #TODO: add check to create_instance to raise error for this image ID?
155
+ :name => opts[:name],
156
+ :description => opts[:description],
157
+ :state => 'PENDING',
158
+ :hardware_profiles => hwps
159
+ )
160
+ end
161
+ end
162
+
163
+ def destroy_image(credentials, image_id)
164
+ safely do
165
+ client = new_client(credentials)
166
+ client.unregister_disk_image(image_id)
167
+ end
168
+ end
169
+
170
+ ######################################################################
171
+ # Realms
172
+ ######################################################################
173
+ def realms(credentials, opts={})
174
+ realms = []
175
+ safely do
176
+ client = new_client(credentials)
177
+ # opts may include scope: system, network
178
+ # first retrieve list of VSYS ids (and add if scope is not only filtering for network)
179
+ if opts and opts[:id]
180
+
181
+ # determine id belongs to vsys or network
182
+ vsys_id = client.extract_vsys_id(opts[:id])
183
+ vsys = client.get_vsys_attributes(vsys_id)['vsys'][0]
184
+ realm_name = vsys['vsysName'][0]
185
+ limit = '[VSYS]'
186
+ if opts[:id] != vsys_id # network id specified
187
+ opts[:id] =~ /^.*\b(\w+)$/
188
+ realm_name += ' [' + $1 + ']' # vsys name or vsys name + network [DMZ/SECURE1/SECURE2]
189
+ limit = '[Network segment]'
190
+ end
191
+ realms << Realm::new(
192
+ :id => opts[:id],
193
+ :name => realm_name,
194
+ #:limit => :unlimited,
195
+ :limit => limit,
196
+ :state => 'AVAILABLE' # map to state of FW/VSYS (reconfiguring = unavailable)?
197
+ # :scope => 'system'
198
+ )
199
+ elsif xml = client.list_vsys['vsyss']
200
+
201
+ xml[0]['vsys'].each do |vsys|
202
+
203
+ realms << Realm::new(
204
+ :id => vsys['vsysId'][0], # vsysId or networkId
205
+ :name => vsys['vsysName'][0], # vsys name or vsys name + network (DMZ/SECURE1/SECURE2)
206
+ #:limit => :unlimited,
207
+ :limit => '[VSYS]',
208
+ :state => 'AVAILABLE' # map to state of FW/VSYS (reconfiguring = unavailable)?
209
+ # :scope => 'system'
210
+ )
211
+ # then retrieve and add list of network segments
212
+ client.get_vsys_configuration(vsys['vsysId'][0])['vsys'][0]['vnets'][0]['vnet'].each do |vnet|
213
+
214
+ vnet['networkId'][0] =~ /^.*\b(\w+)$/
215
+ realm_name = vsys['vsysName'][0].to_s + ' [' + $1 + ']' # vsys name or vsys name + network [DMZ/SECURE1/SECURE2]
216
+ realms << Realm::new(
217
+ :id => vnet['networkId'][0], # vsysId or networkId
218
+ :name => realm_name,
219
+ #:limit => :unlimited,
220
+ :limit => '[Network segment]',
221
+ :state => 'AVAILABLE' # map to state of FW/VSYS (reconfiguring = unavailable)?
222
+ # :scope => 'network'
223
+ )
224
+ end
225
+ end
226
+ end
227
+ end
228
+ filter_on(realms, :id, opts)
229
+ end
230
+
231
+ ######################################################################
232
+ # Instances
233
+ ######################################################################
234
+ def instances(credentials, opts={})
235
+ instances = []
236
+
237
+ safely do
238
+ client = new_client(credentials)
239
+
240
+ if opts and opts[:id]
241
+ vsys_id = client.extract_vsys_id(opts[:id])
242
+ vsys_config = client.get_vsys_configuration(vsys_id)
243
+ vsys_config['vsys'][0]['vservers'][0]['vserver'].each do |vserver|
244
+ if vserver['vserverId'][0] == opts[:id]
245
+
246
+ # check state first as it may be filtered on
247
+ state_data = instance_state_data(vserver, client)
248
+ if opts[:state].nil? or opts[:state] == state_data[:state]
249
+
250
+ instance = convert_to_instance(client, vserver, state_data)
251
+ add_instance_details(instance, client, vserver)
252
+
253
+ instances << instance
254
+ end
255
+ end
256
+ end
257
+ elsif xml = client.list_vsys['vsyss']
258
+
259
+ xml[0]['vsys'].each do |vsys|
260
+
261
+ # use get_vsys_configuration (instead of get_vserver_configuration) to retrieve all vservers in one call
262
+ vsys_config = client.get_vsys_configuration(vsys['vsysId'][0])
263
+ vsys_config['vsys'][0]['vservers'][0]['vserver'].each do |vserver|
264
+
265
+ # to keep the response time of this method acceptable, retrieve state
266
+ # only if required because state is filtered on
267
+ state_data = opts[:state] ? instance_state_data(vserver, client) : nil
268
+ # filter on state
269
+ if opts[:state].nil? or opts[:state] == state_data[:state]
270
+ instances << convert_to_instance(client, vserver, state_data)
271
+ end
272
+ end
273
+ end
274
+ end
275
+ end
276
+ instances = filter_on( instances, :state, opts )
277
+ filter_on( instances, :id, opts )
278
+ end
279
+
280
+ # Start an instance, given its id.
281
+ def start_instance(credentials, id)
282
+ safely do
283
+ client = new_client(credentials)
284
+ if id =~ /^.*-S-0001/ # FW
285
+ client.start_efm(id)
286
+ else
287
+ # vserver or SLB (no way to tell which from id)
288
+ begin
289
+ client.start_vserver(id)
290
+ rescue Exception => ex
291
+ # if not found, try starting as SLB
292
+ if not ex.message =~ /VALIDATION_ERROR.*/
293
+ raise ex
294
+ else
295
+ begin
296
+ client.start_efm(id)
297
+ rescue
298
+ # if that fails as well, just raise the original error
299
+ raise ex
300
+ end
301
+ end
302
+ end
303
+ end
304
+ end
305
+ instances(credentials, {:id => id}).first
306
+ end
307
+
308
+ # Stop an instance, given its id.
309
+ def stop_instance(credentials, id)
310
+ safely do
311
+ client = new_client(credentials)
312
+ if id =~ /^.*-S-0001/ # FW
313
+ client.stop_efm(id)
314
+ else
315
+ # vserver or SLB (no way to tell which from id)
316
+ begin
317
+ client.stop_vserver(id)
318
+ rescue Exception => ex
319
+ #if not found, try stopping as SLB
320
+ if not ex.message =~ /VALIDATION_ERROR.*/
321
+ raise ex
322
+ else
323
+ begin
324
+ client.stop_efm(id)
325
+ rescue
326
+ # if that fails as well, just raise the original error
327
+ raise ex
328
+ end
329
+ end
330
+ end
331
+ end
332
+ end
333
+ instances(credentials, {:id => id}).first
334
+ end
335
+
336
+ # FGCP has no API for reboot
337
+ # def reboot_instance(credentials, id)
338
+ # raise 'Reboot action not supported'
339
+ # end
340
+
341
+ # Create a new instance, given an image id
342
+ # opts can include an optional name for the instance, hardware profile (hwp_id) and realm_id
343
+ def create_instance(credentials, image_id, opts={})
344
+ name = opts[:name]
345
+ # default to 'economy' or obtain latest hardware profiles and pick the lowest spec profile?
346
+ hwp = opts[:hwp_id] || 'economy'
347
+ network_id = opts[:realm_id]
348
+ safely do
349
+ client = new_client(credentials)
350
+ xml = client.create_vserver(name, hwp, image_id, network_id)
351
+ # returns vserver details
352
+ instances(credentials, {:id => xml['vserverId'][0]}).first
353
+ end
354
+ end
355
+
356
+ # Destroy an instance, given its id.
357
+ def destroy_instance(credentials, id)
358
+ safely do
359
+ client = new_client(credentials)
360
+ vsys_id = client.extract_vsys_id(id)
361
+ if id == "#{vsys_id}-S-0001" # if FW
362
+ client.destroy_vsys(vsys_id)
363
+ else
364
+ # vserver or SLB (no way to tell which from id)
365
+ begin
366
+ client.destroy_vserver(id)
367
+ rescue Exception => ex
368
+ # if not found, try destroying as SLB
369
+ if not ex.message =~ /VALIDATION_ERROR.*/
370
+ raise ex
371
+ else
372
+ begin
373
+ client.destroy_efm(id)
374
+ rescue
375
+ # if that fails as well, just raise the original error
376
+ raise ex
377
+ end
378
+ end
379
+ end
380
+ end
381
+ end
382
+ end
383
+
384
+ def run_on_instance(credentials, opts={})
385
+ target = instance(credentials, opts)
386
+ safely do
387
+ param = {}
388
+ param[:port] = opts[:port] || '22'
389
+ param[:ip] = opts[:ip] || target.public_addresses.first.address
390
+ param[:credentials] = { :username => target.username }
391
+
392
+ if opts[:private_key] and opts[:private_key].length > 1
393
+ param[:private_key] = opts[:private_key]
394
+ else
395
+ password = (opts[:password] and opts[:password].length > 0) ? opts[:password] : target.password
396
+ param[:credentials].merge!({ :password => password })
397
+ end
398
+
399
+ Deltacloud::Runner.execute(opts[:cmd], param)
400
+ end
401
+ end
402
+
403
+ ######################################################################
404
+ # Storage volumes
405
+ ######################################################################
406
+ def storage_volumes(credentials, opts={})
407
+ volumes = []
408
+ safely do
409
+ client = new_client(credentials)
410
+ if opts and opts[:id]
411
+ vdisk = client.get_vdisk_attributes(opts[:id])['vdisk'][0]
412
+ state = client.get_vdisk_status(opts[:id])['vdiskStatus'][0]
413
+ actions = []
414
+ if state == 'NORMAL'
415
+ if vdisk['attachedTo'].nil?
416
+ state = 'AVAILABLE'
417
+ actions = [:attach, :destroy]
418
+ else
419
+ state = 'IN-USE'
420
+ actions = [:detach]
421
+ end
422
+ end
423
+
424
+ volumes << StorageVolume.new(
425
+ :id => opts[:id],
426
+ :name => vdisk['vdiskName'][0],
427
+ :capacity => vdisk['size'][0],
428
+ :instance_id => vdisk['attachedTo'].nil? ? nil : vdisk['attachedTo'][0],
429
+ :state => state,
430
+ :actions => actions,
431
+ # alining with rhevm, which returns 'system' or 'data'
432
+ :kind => determine_storage_type(opts[:id]),
433
+ :realm_id => client.extract_vsys_id(opts[:id])
434
+ )
435
+ elsif xml = client.list_vsys['vsyss']
436
+
437
+ xml[0]['vsys'].each do |vsys|
438
+
439
+ vdisks = client.list_vdisk(vsys['vsysId'][0])['vdisks'][0]
440
+
441
+ if vdisks['vdisk']
442
+ vdisks['vdisk'].each do |vdisk|
443
+
444
+ #state requires an additional call per volume. Only set if attached.
445
+ #exclude system disks as they are not detachable?
446
+ volumes << StorageVolume.new(
447
+ :id => vdisk['vdiskId'][0],
448
+ :name => vdisk['vdiskName'][0],
449
+ :capacity => vdisk['size'][0],
450
+ :instance_id => vdisk['attachedTo'].nil? ? nil : vdisk['attachedTo'][0],
451
+ :realm_id => client.extract_vsys_id(vdisk['vdiskId'][0]),
452
+ # alining with rhevm, which returns 'system' or 'data'
453
+ :kind => determine_storage_type(vdisk['vdiskId'][0]),
454
+ :state => vdisk['attachedTo'].nil? ? nil : 'IN-USE'
455
+ )
456
+ end
457
+ end
458
+ end
459
+ end
460
+ end
461
+ volumes
462
+ end
463
+
464
+ def create_storage_volume(credentials, opts={})
465
+ opts ||= {}
466
+ opts[:name] ||= Time.now.to_s
467
+ opts[:capacity] ||= '1' # DC default
468
+ #size has to be a multiple of 10: round up.
469
+ opts[:capacity] = ((opts[:capacity].to_f / 10.0).ceil * 10.0).to_s
470
+
471
+ safely do
472
+ client = new_client(credentials)
473
+
474
+ if opts[:realm_id]
475
+ # just in case the user got confused and specified a network id
476
+ opts[:realm_id] = client.extract_vsys_id(opts[:realm_id])
477
+ elsif xml = client.list_vsys['vsyss']
478
+
479
+ # use first vsys returned as realm
480
+ opts[:realm_id] = xml[0]['vsys'][0]['vsysId'][0]
481
+ end
482
+
483
+ vdisk_id = client.create_vdisk(opts[:realm_id], opts[:name], opts[:capacity])['vdiskId'][0]
484
+
485
+ StorageVolume.new(
486
+ :id => vdisk_id,
487
+ :created => Time.now.to_s,
488
+ :name => opts[:name],
489
+ :capacity => opts[:capacity],
490
+ :realm_id => client.extract_vsys_id(opts[:realm_id]),
491
+ :instance_id => nil,
492
+ :state => 'DEPLOYING',
493
+ # alining with rhevm, which returns 'system' or 'data'
494
+ :kind => 'data',
495
+ :actions => []
496
+ )
497
+ end
498
+ end
499
+
500
+ def destroy_storage_volume(credentials, opts={})
501
+ safely do
502
+ client = new_client(credentials)
503
+ client.destroy_vdisk(opts[:id])
504
+ end
505
+ end
506
+
507
+ def attach_storage_volume(credentials, opts={})
508
+ safely do
509
+ client = new_client(credentials)
510
+ client.attach_vdisk(opts[:instance_id], opts[:id])
511
+ end
512
+ storage_volumes(credentials, opts).first
513
+ end
514
+
515
+ def detach_storage_volume(credentials, opts={})
516
+ safely do
517
+ client = new_client(credentials)
518
+ client.detach_vdisk(opts[:instance_id], opts[:id])
519
+ end
520
+ storage_volumes(credentials, opts)
521
+ end
522
+
523
+ ######################################################################
524
+ # Storage Snapshots
525
+ ######################################################################
526
+ def storage_snapshots(credentials, opts={})
527
+ snapshots = []
528
+
529
+ safely do
530
+ client = new_client(credentials)
531
+ if opts and opts[:id]
532
+ vdisk_id, backup_id = split_snapshot_id(opts[:id])
533
+
534
+ if backups = client.list_vdisk_backup(vdisk_id)['backups']
535
+
536
+ backups[0]['backup'].each do |backup|
537
+
538
+ snapshots << StorageSnapshot.new(
539
+ :id => opts[:id],
540
+ #:state => ?,
541
+ :storage_volume_id => vdisk_id,
542
+ :created => backup['backupTime'][0]
543
+ ) if backup_id = backup['backupId'][0]
544
+ end
545
+ end
546
+ elsif xml = client.list_vsys['vsyss']
547
+
548
+ xml[0]['vsys'].each do |vsys|
549
+
550
+ vdisks = client.list_vdisk(vsys['vsysId'][0])['vdisks'][0]
551
+ if vdisks['vdisk']
552
+ vdisks['vdisk'].each do |vdisk|
553
+
554
+ #TODO: skipping system disks for now to improve performance
555
+ if vdisk['vdiskId'][0] =~ /^.*-D-\d\d\d\d/
556
+
557
+ backups = client.list_vdisk_backup(vdisk['vdiskId'][0])
558
+ if backups['backups'] and backups['backups'][0]['backup']
559
+ backups['backups'][0]['backup'].each do |backup|
560
+
561
+ snapshots << StorageSnapshot.new(
562
+ :id => generate_snapshot_id(vdisk['vdiskId'][0], backup['backupId'][0]),
563
+ #:state => ?,
564
+ :storage_volume_id => vdisk['vdiskId'][0],
565
+ :created => backup['backupTime'][0]
566
+ )
567
+ end
568
+ end
569
+ end
570
+ end
571
+ end
572
+ end
573
+ end
574
+ end
575
+
576
+ snapshots
577
+ end
578
+
579
+ def create_storage_snapshot(credentials, opts={})
580
+ safely do
581
+ client = new_client(credentials)
582
+ client.backup_vdisk(opts[:volume_id])
583
+ end
584
+
585
+ StorageSnapshot.new(
586
+ :id => "PENDING-#{opts[:volume_id]}", # don't know id until backup completed
587
+ :state => 'PENDING', # OK to make up a state like that?
588
+ :storage_volume_id => opts[:volume_id],
589
+ :created => Time.now.to_s
590
+ )
591
+ end
592
+
593
+ def destroy_storage_snapshot(credentials, opts={})
594
+ vdisk_id, backup_id = split_snapshot_id(opts[:id])
595
+ safely do
596
+ client = new_client(credentials)
597
+ client.destroy_vdisk_backup(client.extract_vsys_id(opts[:id]), backup_id)
598
+ end
599
+ end
600
+
601
+ ######################################################################
602
+ # Addresses
603
+ ######################################################################
604
+ def addresses(credentials, opts={})
605
+ addrs_to_instance = {}
606
+ ips_per_vsys = {}
607
+ safely do
608
+ client = new_client(credentials)
609
+ opts ||= {}
610
+ public_ips = client.list_public_ips(opts[:realm_id])['publicips']
611
+ return [] if public_ips.nil? or public_ips[0]['publicip'].nil?
612
+
613
+ # first discover the VSYS each address belongs to
614
+ public_ips[0]['publicip'].each do |ip|
615
+ if not opts[:id] or opts[:id] == ip['address'][0]
616
+
617
+ ips_per_vsys[ip['vsysId'][0]] ||= []
618
+ ips_per_vsys[ip['vsysId'][0]] << ip['address'][0]
619
+ end
620
+ end
621
+
622
+ ips_per_vsys.each_pair do |vsys_id, ips|
623
+ #nat rules show both mapped and unmapped IP addresses
624
+ #may not have privileges to view nat rules on this vsys
625
+ begin
626
+ fw_id = "#{vsys_id}-S-0001"
627
+ nat_rules = client.get_efm_configuration(fw_id, 'FW_NAT_RULE')['efm'][0]['firewall'][0]['nat'][0]['rules'][0]
628
+ rescue RuntimeError => ex
629
+ raise ex unless ex.message =~ /^(ACCESS_NOT_PERMIT).*/
630
+ end
631
+
632
+ if nat_rules and nat_rules['rule']
633
+ # collect all associated IP addresses (pub->priv) in vsys
634
+ associated_ips = {}
635
+
636
+ nat_rules['rule'].each do |rule|
637
+ if opts[:id].nil? or opts[:id] == rule['publicIp'][0] # filter on public IP if specified
638
+ associated_ips[rule['publicIp'][0]] = rule['privateIp'][0] if rule['privateIp']
639
+ end
640
+ end
641
+
642
+ # each associated target private IP belongs to either a vserver or SLB
643
+ # 1. for vservers, obtain all ids from get_vsys_configuration in one call
644
+ vsys = client.get_vsys_configuration(vsys_id)
645
+ vsys['vsys'][0]['vservers'][0]['vserver'].each do |vserver|
646
+
647
+ if determine_server_type(vserver) == 'vserver'
648
+ vnic = vserver['vnics'][0]['vnic'][0]
649
+
650
+ associated_ips.find do |pub,priv|
651
+ addrs_to_instance[pub] = vserver['vserverId'][0] if priv == vnic['privateIp'][0]
652
+ end if vnic['privateIp'] # when an instance is being created, the private ip is not known yet
653
+
654
+ end
655
+ end # of loop over vsys' vservers
656
+
657
+ # 2. for slbs, obtain all ids from list_efm
658
+ if addrs_to_instance.keys.size < associated_ips.keys.size # only if associated ips left to process
659
+
660
+ if slbs = client.list_efm(vsys_id, 'SLB')['efms']
661
+ slbs[0]['efm'].find do |slb|
662
+
663
+ associated_ips.find do |pub,priv|
664
+ addrs_to_instance[pub] = slb['efmId'][0] if priv == slb['slbVip'][0]
665
+ end
666
+ addrs_to_instance.keys.size < associated_ips.keys.size # stop if no associated ips left to process
667
+ end
668
+ end
669
+ end
670
+ end # of nat_rules has rules
671
+ end # of ips_per_vsys.each
672
+ end
673
+
674
+ addresses = []
675
+ ips_per_vsys.values.each do |pubs|
676
+ addresses += pubs.collect do |pub|
677
+ Address.new(:id => pub, :instance_id => addrs_to_instance[pub])
678
+ end
679
+ end
680
+ addresses
681
+ end
682
+
683
+ def address(credentials, opts={})
684
+ addresses(credentials, opts).first
685
+ end
686
+
687
+ def create_address(credentials, opts={})
688
+ safely do
689
+ client = new_client(credentials)
690
+ opts ||= {}
691
+ if opts[:realm_id]
692
+ # just in case a network realm was passed
693
+ opts[:realm_id] = client.extract_vsys_id(opts[:realm_id])
694
+ else
695
+ # get first vsys
696
+ xml = client.list_vsys
697
+ opts[:realm_id] = xml['vsyss'][0]['vsys'][0]['vsysId'][0] if xml['vsyss']
698
+ end
699
+
700
+ client.allocate_public_ip(opts[:realm_id])
701
+ end
702
+ #TODO: new address not returned immediately!
703
+ Address.new(:id => 'PENDING-xxx.xxx.xxx.xxx')
704
+ end
705
+
706
+ def destroy_address(credentials, opts={})
707
+ opts ||= {}
708
+ safely do
709
+ client = new_client(credentials)
710
+ if opts[:realm_id]
711
+ opts[:realm_id] = client.extract_vsys_id(opts[:realm_id])
712
+ else
713
+ xml = client.list_public_ips
714
+ if xml['publicips']
715
+ xml['publicips'][0]['publicip'].find do |ip|
716
+ opts[:realm_id] = ip['vsysId'][0] if opts[:id] == ip['address'][0]
717
+ end
718
+ end
719
+ end
720
+ begin
721
+ # detach just in case
722
+ client.detach_public_ip(opts[:realm_id], opts[:id])
723
+ rescue Exception => ex
724
+ raise ex unless ex.message =~ /^ALREADY_DETACHED.*/
725
+ end
726
+ client.free_public_ip(opts[:realm_id], opts[:id])
727
+ end
728
+ end
729
+
730
+ def associate_address(credentials, opts={})
731
+ safely do
732
+ client = new_client(credentials)
733
+ vsys_id = client.extract_vsys_id(opts[:instance_id])
734
+
735
+ begin
736
+ client.attach_public_ip(vsys_id, opts[:id])
737
+ rescue Exception => ex
738
+ raise ex unless ex.message =~ /^ALREADY_ATTACHED.*/
739
+ end
740
+
741
+ # retrieve private address
742
+ # use get_vsys_configuration (instead of get_vserver_configuration) to also know if instance is an SLB
743
+ vsys_config = client.get_vsys_configuration(vsys_id)
744
+ vserver = vsys_config['vsys'][0]['vservers'][0]['vserver'].find { |e| e['vserverId'][0] == opts[:instance_id] }
745
+
746
+ case determine_server_type(vserver)
747
+ when 'vserver'
748
+ private_ip = vserver['vnics'][0]['vnic'][0]['privateIp'][0]
749
+ when 'SLB'
750
+ if slbs = client.list_efm(vsys_id, 'SLB')['efms']
751
+ private_ip = slbs[0]['efm'].find { |slb| slb['slbVip'][0] if slb['efmId'][0] == opts[:instance_id] }
752
+ end
753
+ end if vserver
754
+
755
+ fw_id = "#{vsys_id}-S-0001"
756
+ nat_rules = client.get_efm_configuration(fw_id, 'FW_NAT_RULE')['efm'][0]['firewall'][0]['nat'][0]['rules'][0]
757
+
758
+ if nat_rules and not nat_rules.empty? # happens only if no enabled IP address?
759
+
760
+ nat_rules['rule'].each do |rule|
761
+
762
+ if rule['publicIp'][0] == opts[:id]
763
+ rule['privateIp'] = [ private_ip ]
764
+ rule['snapt'] = [ 'true' ]
765
+ else
766
+ rule['snapt'] = [ 'false' ]
767
+ end
768
+ end
769
+ end
770
+
771
+ new_rules = {
772
+ 'configuration' => [
773
+ 'firewall_nat' => [nat_rules]
774
+ ]}
775
+
776
+ # create FW configuration xml file with new rules
777
+ conf_xml_new = XmlSimple.xml_out(new_rules,
778
+ 'RootName' => 'Request'
779
+ )
780
+ client.update_efm_configuration(fw_id, 'FW_NAT_RULE', conf_xml_new)
781
+
782
+ Address.new(:id => opts[:id], :instance_id => opts[:instance_id])
783
+ end
784
+ end
785
+
786
+ def disassociate_address(credentials, opts={})
787
+ safely do
788
+ client = new_client(credentials)
789
+
790
+ if not opts[:realm_id]
791
+
792
+ if public_ips = client.list_public_ips['publicips']
793
+
794
+ public_ips[0]['publicip'].find do |ip|
795
+ opts[:realm_id] = ip['vsysId'][0] if opts[:id] == ip['address'][0]
796
+ end
797
+ end
798
+ end
799
+
800
+ vsys_id = client.extract_vsys_id(opts[:realm_id])
801
+ fw_id = "#{vsys_id}-S-0001"
802
+ nat_rules = client.get_efm_configuration(fw_id, 'FW_NAT_RULE')['efm'][0]['firewall'][0]['nat'][0]['rules'][0]
803
+
804
+ if nat_rules and not nat_rules.empty? # happens only if no enabled IP address?
805
+
806
+ nat_rules['rule'].reject! { |rule| rule['publicIp'][0] == opts[:id] }
807
+ end
808
+
809
+ new_rules = {
810
+ 'configuration' => [
811
+ 'firewall_nat' => [nat_rules]
812
+ ]}
813
+
814
+ # create FW configuration xml file with new rules
815
+ conf_xml_new = XmlSimple.xml_out(new_rules,
816
+ 'RootName' => 'Request'
817
+ )
818
+
819
+ client.update_efm_configuration(fw_id, 'FW_NAT_RULE', conf_xml_new)
820
+ client.detach_public_ip(client.extract_vsys_id(opts[:realm_id]), opts[:id])
821
+ end
822
+ end
823
+
824
+ ######################################################################
825
+ # Firewalls
826
+ ######################################################################
827
+ def firewalls(credentials, opts={})
828
+ firewalls = []
829
+ fw_name = 'Firewall' # currently always 'Firewall'
830
+
831
+ safely do
832
+ client = new_client(credentials)
833
+ if opts and opts[:id]
834
+ # get details incl. rules on single FW
835
+ rules = []
836
+
837
+ configuration_xml = <<-"eofwpxml"
838
+ <?xml version="1.0" encoding ="UTF-8"?>
839
+ <Request>
840
+ <configuration>
841
+ <firewall_policy>
842
+ </firewall_policy>
843
+ </configuration>
844
+ </Request>
845
+ eofwpxml
846
+
847
+ fw = client.get_efm_configuration(opts[:id], 'FW_POLICY', configuration_xml)
848
+ fw_name = fw['efm'][0]['efmName'][0] # currently always 'Firewall'
849
+ fw_owner_id = fw['efm'][0]['creator'][0]
850
+
851
+ fw['efm'][0]['firewall'][0]['directions'][0]['direction'].each do |direction|
852
+
853
+ direction['policies'][0]['policy'].each do |policy|
854
+
855
+ sources = []
856
+ ['src', 'dst'].each do |e|
857
+
858
+ if policy[e] and policy[e][0] and not policy[e][0].empty?
859
+
860
+ ip_address_type = policy["#{e}Type"][0]
861
+ address = policy[e][0]
862
+ address.sub!('any', '0.0.0.0/0') if ip_address_type == 'IP'
863
+ address += '/32' if ip_address_type == 'IP' and not address =~ /.*\/.*/
864
+
865
+ sources << {
866
+ :type => 'address',
867
+ :family => 'ipv4',
868
+ :address => address.split('/').first,
869
+ :prefix => ip_address_type == 'IP' ? address.split('/').last : nil
870
+ }
871
+ end
872
+ end
873
+
874
+ # defining ingress as access going from Internet/Intranet -> DMZ -> SECURE1 -> SECURE2
875
+ ingress = policy['id'][0] =~ /[13].*/ ? 'ingress' : 'egress'
876
+
877
+ rules << FirewallRule.new({
878
+ :id => policy['id'][0],
879
+ :allow_protocol => policy['protocol'][0],
880
+ :port_from => policy['srcPort'] ? policy['srcPort'][0] : nil, # not set for e.g. ICMP
881
+ :port_to => policy['dstPort'] ? policy['dstPort'][0] : nil, # not set for e.g. ICMP
882
+ :direction => ingress,
883
+ :sources => sources
884
+ }) unless policy['action'][0] == 'Deny' or policy['id'][0] == '50000' # exclude special case
885
+ end
886
+ end
887
+
888
+ vsys = client.get_vsys_attributes(client.extract_vsys_id(opts[:id]))['vsys'][0]
889
+ firewalls << Firewall.new({
890
+ :id => opts[:id],
891
+ :name => fw_name,
892
+ :description => "#{vsys['vsysName'][0]} [#{vsys['baseDescriptor'][0]}]",
893
+ :owner_id => fw_owner_id,
894
+ :rules => rules
895
+ })
896
+ else
897
+ xml = client.list_vsys
898
+ return [] if xml['vsyss'].nil?
899
+
900
+ firewalls = xml['vsyss'][0]['vsys'].collect do |vsys|
901
+
902
+ Firewall.new({
903
+ :id => vsys['vsysId'][0] + '-S-0001',
904
+ :name => fw_name,
905
+ :description => "#{vsys['vsysName'][0]} [#{vsys['baseDescriptor'][0]}]",
906
+ :owner_id => vsys['creator'][0]
907
+ })
908
+ end
909
+ end
910
+ end
911
+
912
+ firewalls
913
+ end
914
+
915
+ def create_firewall(credentials, opts={})
916
+ safely do
917
+ client = new_client(credentials)
918
+ # using 'description' as vsysDescriptor
919
+ vsys_id = client.create_vsys(opts['description'], opts['name'])['vsysId'][0]
920
+ fw_id = vsys_id + '-S-0001'
921
+ Firewall.new({
922
+ :id => fw_id,
923
+ :name => opts['name'],
924
+ :description => opts['description'],
925
+ :owner_id => '',
926
+ :rules => []
927
+ })
928
+ end
929
+ end
930
+
931
+ def delete_firewall(credentials, opts={})
932
+ safely do
933
+ client = new_client(credentials)
934
+ client.destroy_vsys(client.extract_vsys_id(opts[:id]))
935
+ end
936
+ end
937
+
938
+ #TODO
939
+ # def create_firewall_rule(credentials, opts={})
940
+ # p opts
941
+ # end
942
+
943
+ def delete_firewall_rule(credentials, opts={})
944
+ # retrieve current FW rules, delete rule, send back to API server
945
+ safely do
946
+ client = new_client(credentials)
947
+ conf_xml_old = <<-"eofwopxml"
948
+ <?xml version="1.0" encoding ="UTF-8"?>
949
+ <Request>
950
+ <configuration>
951
+ <firewall_policy>
952
+ </firewall_policy>
953
+ </configuration>
954
+ </Request>
955
+ eofwopxml
956
+
957
+ # retrieve current rules
958
+ fw = client.get_efm_configuration(opts[:firewall], 'FW_POLICY', conf_xml_old)
959
+ rule50000_log = 'On'
960
+
961
+ # delete specified rule and special rule 50000 (handled later)
962
+ fw['efm'][0]['firewall'][0]['directions'][0]['direction'].reject! do |direction|
963
+
964
+ direction['policies'][0]['policy'].reject! do |policy|
965
+
966
+ rule_id = policy['id'][0]
967
+ # need to use (final) 3 digit id
968
+ policy['id'][0] = rule_id[2..4]
969
+ # storage rule 50000's log attribute for later
970
+ rule50000_log = policy['log'][0] if rule_id == '50000'
971
+ # some elements not allowed if service is NTP, DNS, etc.
972
+ if not policy['dstService'][0] == 'NONE'
973
+ policy.delete('dstType')
974
+ policy.delete('dstPort')
975
+ policy.delete('protocol')
976
+ end
977
+ rule_id == opts[:rule_id] or rule_id == '50000'
978
+ end
979
+
980
+ direction['policies'][0]['policy'].empty?
981
+ end
982
+
983
+ # add entry for 50000 special rule
984
+ fw['efm'][0]['firewall'][0]['directions'][0]['direction'] << {
985
+ 'policies' => [
986
+ 'policy' => [
987
+ 'log' => [ rule50000_log ]
988
+ ]
989
+ ]
990
+ }
991
+
992
+ new_rules = {
993
+ 'configuration' => [
994
+ 'firewall_policy' => [
995
+ 'directions' => fw['efm'][0]['firewall'][0]['directions']
996
+ ]
997
+ ]}
998
+
999
+ # create FW configuration xml file with new rules
1000
+ conf_xml_new = XmlSimple.xml_out(new_rules,
1001
+ 'RootName' => 'Request'
1002
+ )
1003
+ conf_xml_new.gsub!(/(<(to|from)>).+(INTERNET|INTRANET)/, '\1\3')
1004
+
1005
+ client.update_efm_configuration(opts[:firewall], 'FW_POLICY', conf_xml_new)
1006
+ end
1007
+ end
1008
+
1009
+ ######################################################################
1010
+ # Load Balancers
1011
+ ######################################################################
1012
+ def load_balancers(credentials, opts={})
1013
+ balancers = []
1014
+ safely do
1015
+ client = new_client(credentials)
1016
+ xml = client.list_vsys
1017
+ return [] if xml['vsyss'].nil?
1018
+
1019
+ xml['vsyss'][0]['vsys'].each do |vsys|
1020
+
1021
+ # use get_vsys_configuration (instead of list_efm) to retrieve all SLBs incl. realms in one call
1022
+ vsys_config = client.get_vsys_configuration(vsys['vsysId'][0])
1023
+ vsys_config['vsys'][0]['vservers'][0]['vserver'].each do |vserver|
1024
+
1025
+ if determine_server_type(vserver) == 'SLB'
1026
+ vserver['vnics'][0]['vnic'][0]['networkId'][0] =~ /^.*\b(\w+)$/
1027
+ realm_name = vsys['vsysId'][0] + ' [' + $1 + ']' # vsys name + network [DMZ/SECURE1/SECURE2]
1028
+ realm = Realm::new(
1029
+ :id => vserver['vnics'][0]['vnic'][0]['networkId'][0],
1030
+ :name => realm_name,
1031
+ :limit => '[Network segment]',
1032
+ :state => 'AVAILABLE' # map to state of FW/VSYS (reconfiguring = unavailable)?
1033
+ )
1034
+ balancer = LoadBalancer.new({
1035
+ :id => vserver['vserverId'][0],
1036
+ :realms => [realm],
1037
+ :listeners => [],
1038
+ :instances => [],
1039
+ :public_addresses => []
1040
+ })
1041
+ balancers << balancer
1042
+ end
1043
+ end
1044
+ end
1045
+ end
1046
+ balancers
1047
+ end
1048
+
1049
+ def load_balancer(credentials, opts={})
1050
+ balancer = nil
1051
+ safely do
1052
+ client = new_client(credentials)
1053
+
1054
+ # use get_vsys_configuration (instead of list_efm) to retrieve all SLBs incl. realms in one call?
1055
+ vsys_id = client.extract_vsys_id(opts[:id])
1056
+ vsys_config = client.get_vsys_configuration(vsys_id)
1057
+
1058
+ vsys_config['vsys'][0]['vservers'][0]['vserver'].each do |vserver|
1059
+
1060
+ if vserver['vserverId'][0] == opts[:id]
1061
+ vserver['vnics'][0]['vnic'][0]['networkId'][0] =~ /^.*\b(\w+)$/
1062
+ realm_name = vsys_id + ' [' + $1 + ']' # vsys name + network [DMZ/SECURE1/SECURE2]
1063
+ realm = Realm::new(
1064
+ :id => vserver['vnics'][0]['vnic'][0]['networkId'][0],
1065
+ :name => realm_name,
1066
+ :limit => '[Network segment]',
1067
+ :state => 'AVAILABLE' # map to state of FW/VSYS (reconfiguring = unavailable)?
1068
+ )
1069
+ balancer = LoadBalancer.new({
1070
+ :id => vserver['vserverId'][0],
1071
+ :realms => [realm],
1072
+ :listeners => [],
1073
+ :instances => [],
1074
+ :public_addresses => []
1075
+ })
1076
+ begin
1077
+ slb_rule = client.get_efm_configuration(opts[:id], 'SLB_RULE')
1078
+ if slb_rule['efm'][0]['loadbalancer'][0]['groups']
1079
+
1080
+ slb_rule['efm'][0]['loadbalancer'][0]['groups'][0]['group'].each do |group|
1081
+
1082
+ group['targets'][0]['target'].each do |server|
1083
+
1084
+ balancer.instances << Instance::new(
1085
+ :id => server['serverId'][0],
1086
+ :name => server['serverName'][0],
1087
+ :realm_id => realm,
1088
+ :private_addresses => [InstanceAddress.new(server['ipAddress'][0])]
1089
+ )
1090
+
1091
+ balancer.add_listener({
1092
+ :protocol => slb_rule['efm'][0]['loadbalancer'][0]['groups'][0]['group'][0]['protocol'][0],
1093
+ :load_balancer_port => slb_rule['efm'][0]['loadbalancer'][0]['groups'][0]['group'][0]['port1'][0],
1094
+ :instance_port => server['port1'][0]
1095
+ })
1096
+ end
1097
+ end
1098
+ end
1099
+
1100
+ slb_vip = slb_rule['efm'][0]['slbVip'][0]
1101
+ opts[:id] =~ /^(.*-S-)\d\d\d\d/
1102
+ fw_id = $1 + '0001'
1103
+ nat_rules = client.get_efm_configuration(fw_id, 'FW_NAT_RULE')['efm'][0]['firewall'][0]['nat'][0]['rules'][0]
1104
+ if nat_rules and not nat_rules.empty?
1105
+ nat_rules['rule'].each do |rule|
1106
+ balancer.public_addresses << InstanceAddress.new(rule['publicIp'][0]) if rule['privateIp'] and rule['privateIp'][0] == slb_vip
1107
+ end
1108
+ end
1109
+ rescue Exception => ex
1110
+ raise ex unless ex.message =~ /(ACCESS_NOT_PERMIT|ILLEGAL_STATE).*/
1111
+ end
1112
+ end
1113
+ end
1114
+ end
1115
+ balancer
1116
+ end
1117
+
1118
+ def create_load_balancer(credentials, opts={})
1119
+ safely do
1120
+ client = new_client(credentials)
1121
+ # if opts['realm_id'].nil? network id specified, pick first vsys' DMZ?
1122
+ # CreateEFM -vsysId vsysId -efmType SLB -efmName opts['name'] -networkId opts['realm_id']
1123
+ network_id = opts[:realm_id]
1124
+ efm = client.create_efm('SLB', opts[:name], network_id)
1125
+ # [{:load_balancer_port => opts['listener_balancer_port'],
1126
+ # :instance_port => opts['listener_instance_port'],
1127
+ # :protocol => opts['listener_protocol']}]
1128
+ # )
1129
+ load_balancer(credentials, {:id => efm['efmId'][0]})
1130
+ end
1131
+ end
1132
+
1133
+ def destroy_load_balancer(credentials, id)
1134
+ safely do
1135
+ client = new_client(credentials)
1136
+ client.destroy_efm(id)
1137
+ end
1138
+ end
1139
+
1140
+ ######################################################################
1141
+ # Providers
1142
+ ######################################################################
1143
+ def providers(credentials, opts={})
1144
+ cert, key = convert_credentials(credentials)
1145
+ cert.subject.to_s =~ /\b[Cc]=(\w\w)\b/
1146
+ country = $1.downcase
1147
+ endpoint = Deltacloud::Drivers::driver_config[:fgcp][:entrypoints]['default'][country]
1148
+ [
1149
+ Provider.new(
1150
+ :id => "fgcp-#{country}",
1151
+ :name => "Fujitsu Global Cloud Platform - #{country.upcase}",
1152
+ :url => endpoint
1153
+ )
1154
+ ]
1155
+ end
1156
+
1157
+ # following code enables region drop-down box on GUI. No need as retrieving region from cert (subject c)
1158
+ # def configured_providers
1159
+ # Deltacloud::Drivers::driver_config[:fgcp][:entrypoints]['default'].keys
1160
+ # end
1161
+
1162
+ exceptions do
1163
+ on /AuthFailure/ do
1164
+ status 401
1165
+ end
1166
+
1167
+ # if user doesn't have privileges to view or operate a particular resource
1168
+ on /User doesn.t have the right of access./ do
1169
+ status 400
1170
+ end
1171
+
1172
+ # time out of sync with ntp
1173
+ on /VALIDATION_ERROR.*synchronized.*API-Server time/ do
1174
+ status 502
1175
+ end
1176
+
1177
+ # wrong vserverId, etc.
1178
+ on /VALIDATION_ERROR/ do
1179
+ status 404
1180
+ end
1181
+
1182
+ # wrong vdiskId, etc.
1183
+ on /RESOURCE_NOT_FOUND/ do
1184
+ status 404
1185
+ end
1186
+
1187
+ # destroying a running SLB, etc.
1188
+ on /ALREADY_STARTED/ do
1189
+ status 502 #?
1190
+ end
1191
+
1192
+ # trying to start a running vserver, etc.
1193
+ on /ILLEGAL_STATE/ do
1194
+ status 502
1195
+ end
1196
+
1197
+ # endpoint for country of certificate subject not found
1198
+ on /API endpoint not found/ do
1199
+ status 502
1200
+ end
1201
+
1202
+ on /.*/ do
1203
+ status 502 # Provider error
1204
+ end
1205
+ end
1206
+
1207
+ ######################################################################
1208
+ # private
1209
+ ######################################################################
1210
+ private
1211
+
1212
+ def new_client(credentials)
1213
+ cert, key = convert_credentials(credentials)
1214
+ FgcpClient.new(cert, key, api_provider)
1215
+ end
1216
+
1217
+ def convert_credentials(credentials)
1218
+ #username could be 'dkoper'
1219
+ #load dkoper/UserCert.p12 from cert dir
1220
+ begin
1221
+ #p File::join(CERT_DIR, credentials.user, 'UserCert.p12')
1222
+ cert_file = File.open(File::join(CERT_DIR, credentials.user, 'UserCert.p12'), 'rb')
1223
+ rescue Errno::ENOENT => e # file not found
1224
+ raise "AuthFailure: No certificate registered under name \'#{credentials.user}\'"
1225
+ # raise Deltacloud::ExceptionHandler::AuthenticationFailure.new(e, "No certificate registered under name #{credentials.user}")
1226
+ end
1227
+ #TODO: check that cert's cn is indeed the username?
1228
+ #cert.subject=/C=AU/O=Fujitsu Limited/CN=diesk_fast
1229
+ #raise AuthError(?) if not
1230
+
1231
+ begin
1232
+ pkcs12 = OpenSSL::PKCS12.new(cert_file, credentials.password)
1233
+ rescue OpenSSL::PKCS12::PKCS12Error => e
1234
+ raise "AuthFailure: Could not open the certificate \'#{credentials.user}\'. Wrong password? Is it a valid PKCS12 cert? #{e.message}"
1235
+ end
1236
+
1237
+ return pkcs12.certificate, pkcs12.key
1238
+ end
1239
+
1240
+ def instance_state_data(vserver, client)
1241
+ # determine server is FW/SLB by checking vserver_id (0001 for FW) or nicNo (>0)
1242
+ if ['FW', 'SLB'].include? determine_server_type(vserver)
1243
+ state = @@INSTANCE_STATE_MAP[client.get_efm_status(vserver['vserverId'][0])['efmStatus'][0]]
1244
+ create_image = false
1245
+ else
1246
+ # vserver
1247
+ state = @@INSTANCE_STATE_MAP[client.get_vserver_status(vserver['vserverId'][0])['vserverStatus'][0]]
1248
+ create_image = (state == /STOPPED|UNEXPECTED_STOP/)
1249
+ end
1250
+
1251
+ {
1252
+ :create_image => create_image,
1253
+ :actions => instance_actions_for(state),
1254
+ :state => state
1255
+ }
1256
+ end
1257
+
1258
+ def add_instance_details(instance, client, vserver)
1259
+ # instance details (public IPs, password) currently only apply to vservers (and some to SLBs)
1260
+ server = determine_server_type(vserver)
1261
+ if not server == 'FW'
1262
+ if server == 'vserver'
1263
+ # vserver-only details
1264
+ images = client.list_disk_images
1265
+ images['diskimages'][0]['diskimage'].each do |img|
1266
+ if vserver['diskimageId'][0] == img['diskimageId'][0]
1267
+ instance.username = img['osName'][0] =~ /Windows.*/ ? 'Administrator' : 'root'
1268
+ end
1269
+ end
1270
+ instance.password = client.get_vserver_initial_password(vserver['vserverId'][0])['initialPassword'][0]
1271
+ end
1272
+
1273
+ # retrieve SLB's representative IP address
1274
+ if server == 'SLB'
1275
+ vsys_id = client.extract_vsys_id(instance.id)
1276
+ if slbs = client.list_efm(vsys_id, 'SLB')['efms']
1277
+ slbs[0]['efm'].find do |slb|
1278
+ instance.private_addresses << InstanceAddress.new(slb['slbVip'][0], :type => :ipv4) if slb['efmId'][0] == instance.id
1279
+ end
1280
+ end
1281
+ end
1282
+
1283
+ # retrieve mapped public ip addresses for vserver or SLB
1284
+ #may not have privileges to view nat rules on this vsys
1285
+ begin
1286
+ vserver['vserverId'][0] =~ /^(.*-S-)\d\d\d\d/
1287
+ fw_id = $1 + '0001'
1288
+ nat_rules = client.get_efm_configuration(fw_id, 'FW_NAT_RULE')['efm'][0]['firewall'][0]['nat'][0]['rules'][0]
1289
+ rescue RuntimeError => ex
1290
+ raise ex unless ex.message =~ /ACCESS_NOT_PERMIT.*/
1291
+ end
1292
+
1293
+ if nat_rules and not nat_rules.empty?
1294
+ private_ips = instance.private_addresses.collect { |e| e.address }
1295
+ nat_rules['rule'].each do |rule|
1296
+ if rule['privateIp'] and private_ips.include?(rule['privateIp'][0])
1297
+ instance.public_addresses << InstanceAddress.new(rule['publicIp'][0], :type => :ipv4)
1298
+ end
1299
+ end
1300
+ end
1301
+
1302
+ end
1303
+ end
1304
+
1305
+ def convert_to_instance(client, vserver, state_data=nil)
1306
+ state_data ||= {}
1307
+
1308
+ private_ips = []
1309
+ vserver['vnics'][0]['vnic'].each do |vnic|
1310
+ # when an instance is being created, the private ip is not known yet
1311
+ private_ips << InstanceAddress.new(vnic['privateIp'][0], :type => :ipv4) if vnic['privateIp']
1312
+ end
1313
+
1314
+ instance_profile = InstanceProfile::new(vserver['vserverType'][0])
1315
+
1316
+ server = determine_server_type(vserver)
1317
+
1318
+ # realm is vsys for FW and network for vserver or SLB
1319
+ if server == 'FW'
1320
+ realm_id = client.extract_vsys_id(vserver['vserverId'][0])
1321
+ else
1322
+ realm_id = vserver['vnics'][0]['vnic'][0]['networkId'][0]
1323
+ end
1324
+
1325
+ # storage volumes
1326
+ storage_volumes = []
1327
+ # system volume
1328
+ if server == 'vserver'
1329
+ storage_volumes << StorageVolume.new(
1330
+ :id => vserver['vserverId'][0],
1331
+ :name => vserver['vserverName'][0],
1332
+ #:device => '', # no API to retrieve from
1333
+ # :capacity => '10',# or '40', need to check with image (vserver['diskimageId'][0],)
1334
+ :realm_id => realm_id,
1335
+ :instance_id => vserver['vserverId'][0],
1336
+ :state => 'IN-USE',
1337
+ # alining with rhevm, which returns 'system' or 'data'
1338
+ :kind => 'system',
1339
+ :actions => []
1340
+ )
1341
+ end
1342
+ # additional volumes
1343
+ if vserver['vdisks'] and vserver['vdisks'][0]['vdisk']
1344
+ vserver['vdisks'][0]['vdisk'].each do |vdisk|
1345
+
1346
+ actions = state_data[:state] and state_data[:state] == 'STOPPED' ? [:detach] : []
1347
+ storage_volumes << StorageVolume.new(
1348
+ :id => vdisk['vdiskId'][0],
1349
+ :name => vdisk['vdiskName'][0],
1350
+ #:device => '', # no API to retrieve from
1351
+ :capacity => vdisk['size'][0],
1352
+ :realm_id => client.extract_vsys_id(realm_id),
1353
+ :instance_id => vserver['vserverId'][0],
1354
+ :state => 'IN-USE',
1355
+ # alining with rhevm, which returns 'system' or 'data'
1356
+ :kind => 'data',
1357
+ :actions => actions
1358
+ )
1359
+ end
1360
+ end
1361
+
1362
+ instance = {
1363
+ :id => vserver['vserverId'][0],
1364
+ :name => vserver['vserverName'][0],
1365
+ :realm_id => realm_id,
1366
+ :instance_profile => instance_profile,
1367
+ :image_id => vserver['diskimageId'][0],
1368
+ :private_addresses => private_ips,
1369
+ :storage_volumes => storage_volumes.collect { |v| {v.id => v.device} },
1370
+ :firewalls => server != 'FW' ? [client.extract_vsys_id(vserver['vserverId'][0]) + '-S-0001'] : nil,
1371
+ :owner_id => vserver['creator'][0]
1372
+ }
1373
+ instance.merge!( {'create_image' => false}) if not server == 'vserver'
1374
+ instance.merge! state_data
1375
+
1376
+ Instance::new(instance)
1377
+ end
1378
+
1379
+ def generate_snapshot_id(vdisk_id, backup_id)
1380
+ "#{vdisk_id}_#{backup_id}"
1381
+ end
1382
+
1383
+ def split_snapshot_id(snapshot_id)
1384
+ snapshot_id =~ /^(.*-\d\d\d\d)_(\d\d\d\d)/
1385
+ return $1, $2 # vdisk_id, backup_id
1386
+ end
1387
+
1388
+ def successful_action?(xml)
1389
+ xml['responseStatus'].to_s == 'SUCCESS'
1390
+ end
1391
+
1392
+ # determine server is vserver/FW/SLB
1393
+ def determine_server_type(vserver)
1394
+ # check vserver_id (0001 for FW) or nicNo (>0 for SLB)
1395
+ return 'FW' if vserver['vserverId'][0] =~ /^.*-S-0001/
1396
+ return 'SLB' if vserver['vnics'][0]['vnic'][0]['nicNo'][0] != '0'
1397
+ return 'vserver'
1398
+ end
1399
+
1400
+ # determine storage volume type (system or additional storage)
1401
+ def determine_storage_type(id)
1402
+ return 'system' if id =~ /^.*-S-\d\d\d\d/
1403
+ return 'data' if id =~ /^.*-D-\d\d\d\d/
1404
+ return 'unknown'
1405
+ end
1406
+
1407
+ def get_fw_nat_rules_for_vserver(client, vserver)
1408
+ /^(\w+-\w+)\b.*/ =~ vserver['vserverId'][0]
1409
+ vsys_id = $1
1410
+
1411
+ client.get_efm_configuration("#{vsys_id}-S-0001", 'FW_NAT_RULE')
1412
+ end
1413
+
1414
+ # FGCP instance states mapped to DeltaCloud
1415
+ @@INSTANCE_STATE_MAP = {
1416
+ 'DEPLOYING' => 'PENDING',
1417
+ 'RUNNING' => 'RUNNING',
1418
+ 'STOPPING' => 'STOPPING',
1419
+ 'STOPPED' => 'STOPPED',
1420
+ 'STARTING' => 'PENDING', # not sure about this one
1421
+ 'FAILOVER' => 'RUNNING',
1422
+ 'UNEXPECTED_STOP' => 'STOPPED',
1423
+ 'RESTORING' => 'PENDING',
1424
+ 'BACKUP_ING' => 'PENDING',
1425
+ 'ERROR' => 'STOPPED',
1426
+ 'START_ERROR' => 'STOPPED', # not sure about this one
1427
+ 'STOP_ERROR' => 'STOPPING',
1428
+ 'REGISTERING' => 'PENDING',
1429
+ 'CHANGE_TYPE' => 'PENDING'
1430
+ }
1431
+
1432
+ end
1433
+ end
1434
+ end
1435
+ end