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.
- data/NOTICE +1 -1
- data/Rakefile +63 -21
- data/bin/deltacloudd +10 -6
- data/config.ru +62 -6
- data/config/drivers/ec2.yaml +8 -0
- data/config/drivers/fgcp.yaml +13 -0
- data/deltacloud-core.gemspec +10 -2
- data/lib/cimi/collections.rb +58 -0
- data/lib/cimi/collections/address_templates.rb +49 -0
- data/lib/cimi/collections/addresses.rb +74 -0
- data/lib/cimi/collections/cloud_entry_point.rb +37 -0
- data/lib/{deltacloud/helpers/conversion_helper.rb → cimi/collections/entity_metadata.rb} +24 -16
- data/lib/cimi/collections/machine_admins.rb +74 -0
- data/lib/cimi/collections/machine_configurations.rb +49 -0
- data/lib/cimi/collections/machine_images.rb +50 -0
- data/lib/cimi/collections/machines.rb +157 -0
- data/lib/cimi/collections/network_configurations.rb +47 -0
- data/lib/cimi/collections/network_templates.rb +48 -0
- data/lib/cimi/collections/networks.rb +125 -0
- data/lib/cimi/collections/routing_group_templates.rb +47 -0
- data/lib/cimi/collections/routing_groups.rb +48 -0
- data/lib/cimi/collections/volume_configurations.rb +48 -0
- data/lib/cimi/collections/volume_images.rb +50 -0
- data/lib/cimi/collections/volumes.rb +80 -0
- data/lib/cimi/collections/vsp_configurations.rb +48 -0
- data/lib/cimi/collections/vsp_templates.rb +50 -0
- data/lib/cimi/collections/vsps.rb +108 -0
- data/lib/cimi/dependencies.rb +1 -0
- data/lib/cimi/helpers.rb +116 -0
- data/lib/cimi/helpers/cimi_helper.rb +6 -2
- data/lib/cimi/models.rb +72 -0
- data/lib/cimi/{model → models}/action.rb +1 -1
- data/lib/cimi/models/address.rb +72 -0
- data/lib/cimi/models/address_collection.rb +34 -0
- data/lib/cimi/models/address_template.rb +54 -0
- data/lib/cimi/models/address_template_collection.rb +34 -0
- data/lib/cimi/{model → models}/base.rb +2 -2
- data/lib/cimi/{model → models}/cloud_entry_point.rb +3 -5
- data/lib/cimi/{model → models}/entity_metadata.rb +7 -6
- data/lib/cimi/{model → models}/entity_metadata_collection.rb +2 -2
- data/lib/cimi/{model → models}/errors.rb +8 -0
- data/lib/cimi/{model → models}/machine.rb +10 -10
- data/lib/cimi/{model → models}/machine_admin.rb +1 -1
- data/lib/cimi/{model → models}/machine_admin_collection.rb +2 -2
- data/lib/cimi/{model → models}/machine_collection.rb +2 -2
- data/lib/cimi/{model → models}/machine_configuration.rb +9 -6
- data/lib/cimi/{model → models}/machine_configuration_collection.rb +2 -2
- data/lib/cimi/{model → models}/machine_image.rb +1 -1
- data/lib/cimi/{model → models}/machine_image_collection.rb +2 -2
- data/lib/cimi/{model → models}/machine_template.rb +0 -0
- data/lib/cimi/{model → models}/machine_template_collection.rb +2 -2
- data/lib/cimi/models/network.rb +109 -0
- data/lib/cimi/{model → models}/network_collection.rb +2 -2
- data/lib/cimi/{model → models}/network_configuration.rb +3 -4
- data/lib/cimi/{model → models}/network_configuration_collection.rb +2 -2
- data/lib/cimi/models/network_template.rb +36 -0
- data/lib/cimi/models/network_template_collection.rb +35 -0
- data/lib/cimi/models/routing_group.rb +34 -0
- data/lib/cimi/models/routing_group_collection.rb +34 -0
- data/lib/cimi/models/routing_group_template.rb +34 -0
- data/lib/cimi/models/routing_group_template_collection.rb +35 -0
- data/lib/cimi/{model → models}/schema.rb +0 -0
- data/lib/cimi/{model → models}/volume.rb +4 -4
- data/lib/cimi/{model → models}/volume_collection.rb +2 -2
- data/lib/cimi/{model → models}/volume_configuration.rb +1 -1
- data/lib/cimi/{model → models}/volume_configuration_collection.rb +2 -2
- data/lib/cimi/{model → models}/volume_image.rb +1 -1
- data/lib/cimi/{model → models}/volume_image_collection.rb +2 -2
- data/lib/cimi/{model → models}/volume_template.rb +0 -0
- data/lib/cimi/{model → models}/volume_template_collection.rb +2 -2
- data/lib/cimi/models/vsp.rb +102 -0
- data/lib/cimi/models/vsp_collection.rb +34 -0
- data/lib/cimi/models/vsp_configuration.rb +40 -0
- data/lib/cimi/models/vsp_configuration_collection.rb +34 -0
- data/lib/cimi/models/vsp_template.rb +34 -0
- data/lib/cimi/models/vsp_template_collection.rb +34 -0
- data/lib/cimi/server.rb +27 -549
- data/lib/deltacloud/api.rb +79 -0
- data/lib/deltacloud/collections.rb +54 -0
- data/lib/deltacloud/collections/addresses.rb +91 -0
- data/lib/deltacloud/collections/buckets.rb +273 -0
- data/lib/deltacloud/collections/drivers.rb +51 -0
- data/lib/deltacloud/collections/firewalls.rb +116 -0
- data/lib/deltacloud/collections/hardware_profiles.rb +29 -0
- data/lib/deltacloud/collections/images.rb +73 -0
- data/lib/deltacloud/collections/instance_states.rb +59 -0
- data/lib/deltacloud/collections/instances.rb +113 -0
- data/lib/deltacloud/collections/keys.rb +61 -0
- data/lib/deltacloud/collections/load_balancers.rb +102 -0
- data/lib/deltacloud/collections/metrics.rb +28 -0
- data/lib/deltacloud/collections/realms.rb +28 -0
- data/lib/deltacloud/collections/storage_snapshots.rb +51 -0
- data/lib/deltacloud/collections/storage_volumes.rb +99 -0
- data/lib/deltacloud/core_ext.rb +14 -6
- data/lib/deltacloud/core_ext/string.rb +17 -5
- data/lib/deltacloud/drivers.rb +6 -42
- data/lib/deltacloud/drivers/azure/azure_driver.rb +0 -4
- data/lib/deltacloud/{base_driver → drivers}/base_driver.rb +56 -18
- data/lib/deltacloud/drivers/condor/condor_driver.rb +1 -8
- data/lib/deltacloud/drivers/ec2/ec2_driver.rb +142 -33
- data/lib/deltacloud/drivers/eucalyptus/eucalyptus_driver.rb +2 -6
- data/lib/deltacloud/{base_driver → drivers}/exceptions.rb +25 -2
- data/lib/deltacloud/drivers/features.rb +154 -0
- data/lib/deltacloud/drivers/fgcp/fgcp_client.rb +387 -0
- data/lib/deltacloud/drivers/fgcp/fgcp_driver.rb +1435 -0
- data/lib/deltacloud/drivers/gogrid/gogrid_driver.rb +8 -11
- data/lib/deltacloud/drivers/google/google_driver.rb +2 -5
- data/lib/deltacloud/drivers/mock/data/keys/test-key.yml +28 -0
- data/lib/deltacloud/drivers/mock/mock_client.rb +2 -2
- data/lib/deltacloud/drivers/mock/mock_driver.rb +58 -40
- data/lib/deltacloud/drivers/mock/mock_driver_cimi_methods.rb +145 -0
- data/lib/deltacloud/drivers/opennebula/cloud_client.rb +107 -73
- data/lib/deltacloud/drivers/opennebula/occi_client.rb +285 -145
- data/lib/deltacloud/drivers/opennebula/opennebula_driver.rb +189 -126
- data/lib/deltacloud/drivers/openstack/openstack_driver.rb +427 -8
- data/lib/deltacloud/drivers/rackspace/rackspace_driver.rb +7 -9
- data/lib/deltacloud/drivers/rhevm/rhevm_driver.rb +48 -66
- data/lib/deltacloud/drivers/rimuhosting/rimuhosting_client.rb +44 -51
- data/lib/deltacloud/drivers/rimuhosting/rimuhosting_driver.rb +7 -8
- data/lib/deltacloud/drivers/sbc/sbc_client.rb +1 -1
- data/lib/deltacloud/drivers/sbc/sbc_driver.rb +3 -3
- data/lib/deltacloud/drivers/terremark/terremark_driver.rb +8 -8
- data/lib/deltacloud/drivers/vsphere/vsphere_driver.rb +39 -13
- data/lib/deltacloud/drivers/vsphere/vsphere_filemanager.rb +1 -0
- data/lib/deltacloud/helpers.rb +79 -7
- data/lib/{sinatra/lazy_auth.rb → deltacloud/helpers/auth_helper.rb} +5 -9
- data/lib/deltacloud/helpers/{blob_stream.rb → blob_stream_helper.rb} +7 -7
- data/lib/deltacloud/helpers/deltacloud_helper.rb +286 -0
- data/lib/deltacloud/helpers/driver_helper.rb +58 -0
- data/lib/deltacloud/helpers/rabbit_helper.rb +34 -0
- data/lib/{sinatra/url_for.rb → deltacloud/helpers/url_helper.rb} +31 -9
- data/lib/deltacloud/models.rb +19 -17
- data/lib/deltacloud/models/bucket.rb +4 -0
- data/lib/deltacloud/{hardware_profile.rb → models/hardware_profile.rb} +4 -2
- data/lib/deltacloud/models/image.rb +1 -0
- data/lib/deltacloud/models/instance.rb +4 -2
- data/lib/deltacloud/models/instance_address.rb +4 -0
- data/lib/deltacloud/models/key.rb +4 -0
- data/lib/deltacloud/models/metric.rb +40 -0
- data/lib/deltacloud/{state_machine.rb → models/state_machine.rb} +18 -1
- data/lib/deltacloud/server.rb +41 -1222
- data/lib/deltacloud_rack.rb +79 -0
- data/lib/{cimi/model/network_template.rb → ec2/helpers.rb} +7 -10
- data/lib/{deltacloud/backend_capability.rb → ec2/helpers/errors.rb} +24 -29
- data/lib/ec2/helpers/result.rb +31 -0
- data/lib/ec2/query_parser.rb +152 -0
- data/lib/ec2/server.rb +70 -0
- data/lib/{deltacloud.rb → sinatra.rb} +7 -12
- data/lib/sinatra/rack_accept.rb +13 -7
- data/lib/sinatra/rack_driver_select.rb +1 -1
- data/lib/sinatra/rack_matrix_params.rb +1 -3
- data/public/images/metric.png +0 -0
- data/public/javascripts/jquery.mobile-1.0.1.min.js +177 -0
- data/public/stylesheets/jquery.mobile-1.0.1.min.css +2 -0
- data/public/stylesheets/new.css +10 -0
- data/support/fedora/deltacloud-core.spec +1 -1
- data/tests/api/common.rb +1 -0
- data/tests/api/driver_test.rb +79 -0
- data/tests/api/library_test.rb +48 -0
- data/tests/cimi/features/step_definitions/common_steps.rb +2 -2
- data/tests/cimi/features/step_definitions/machines_steps.rb +5 -4
- data/tests/cimi/features/step_definitions/volumes_steps.rb +8 -8
- data/tests/cimi/features/support/env.rb +33 -11
- data/tests/common.rb +7 -8
- data/tests/drivers/ec2/api_test.rb +19 -0
- data/tests/drivers/ec2/common.rb +23 -0
- data/tests/drivers/ec2/drivers_test.rb +120 -0
- data/tests/drivers/ec2/hardware_profiles_test.rb +224 -0
- data/tests/drivers/ec2/images_test.rb +230 -0
- data/tests/drivers/ec2/instances_test.rb +356 -0
- data/tests/drivers/ec2/keys_test.rb +181 -0
- data/tests/drivers/ec2/realms_test.rb +146 -0
- data/tests/drivers/fgcp/api_test.rb +47 -0
- data/tests/drivers/fgcp/hardware_profiles_test.rb +54 -0
- data/tests/drivers/fgcp/realms_test.rb +42 -0
- data/tests/drivers/{rackspace → fgcp}/setup.rb +5 -6
- data/tests/drivers/google/api_test.rb +10 -26
- data/tests/drivers/google/buckets_test.rb +79 -95
- data/tests/drivers/google/common.rb +54 -0
- data/tests/drivers/mock/api_test.rb +4 -127
- data/tests/drivers/mock/buckets_test.rb +195 -0
- data/tests/drivers/mock/common.rb +7 -0
- data/tests/drivers/mock/drivers_test.rb +123 -0
- data/tests/drivers/mock/hardware_profiles_test.rb +190 -100
- data/tests/drivers/mock/images_test.rb +162 -103
- data/tests/drivers/mock/instances_test.rb +310 -220
- data/tests/drivers/mock/keys_test.rb +161 -0
- data/tests/drivers/mock/realms_test.rb +109 -70
- data/tests/drivers/mock/storage_snapshots_test.rb +114 -0
- data/tests/drivers/mock/storage_volumes_test.rb +122 -0
- data/tests/drivers/openstack/api_test.rb +8 -3
- data/tests/drivers/openstack/common.rb +21 -0
- data/tests/drivers/openstack/hardware_profiles_test.rb +20 -9
- data/tests/drivers/openstack/images_test.rb +11 -5
- data/tests/drivers/openstack/instances_test.rb +61 -16
- data/tests/drivers/openstack/realms_test.rb +11 -7
- data/tests/drivers/rackspace/api_test.rb +7 -2
- data/tests/drivers/rackspace/buckets_test.rb +7 -2
- data/tests/drivers/rackspace/common.rb +16 -0
- data/tests/drivers/rackspace/hardware_profiles_test.rb +7 -2
- data/tests/drivers/rackspace/images_test.rb +7 -2
- data/tests/drivers/rackspace/instances_test.rb +10 -5
- data/tests/drivers/rackspace/realms_test.rb +7 -2
- data/tests/drivers/rhevm/api_test.rb +12 -6
- data/tests/drivers/rhevm/{setup.rb → common.rb} +8 -1
- data/tests/drivers/rhevm/hardware_profiles_test.rb +7 -2
- data/tests/drivers/rhevm/images_test.rb +8 -2
- data/tests/drivers/rhevm/instances_test.rb +7 -2
- data/tests/drivers/rhevm/realms_test.rb +7 -2
- data/tests/minitest_common.rb +58 -0
- data/tests/minitest_common_api_test.rb +115 -0
- data/views/addresses/associate.html.haml +1 -1
- data/views/addresses/index.html.haml +1 -1
- data/views/addresses/show.html.haml +2 -3
- data/views/api/show.html.haml +22 -9
- data/views/api/show.xml.haml +8 -7
- data/views/blobs/show.xml.haml +7 -4
- data/views/buckets/new.html.haml +2 -2
- data/views/cimi/errors/401.xml.haml +1 -1
- data/views/error.html.haml +3 -3
- data/views/errors/400.html.haml +1 -1
- data/views/errors/400.xml.haml +1 -2
- data/views/errors/401.html.haml +8 -7
- data/views/errors/401.xml.haml +1 -1
- data/views/errors/404.xml.haml +1 -0
- data/views/errors/500.xml.haml +4 -2
- data/views/{cimi/errors/403.html.haml → errors/501.html.haml} +4 -2
- data/views/errors/501.xml.haml +1 -0
- data/views/errors/502.xml.haml +1 -7
- data/views/{cimi/errors/500.html.haml → errors/504.html.haml} +0 -0
- data/views/errors/504.xml.haml +1 -0
- data/views/firewalls/new.html.haml +2 -2
- data/views/firewalls/new_rule.html.haml +1 -1
- data/views/firewalls/show.html.haml +1 -1
- data/views/hardware_profiles/index.html.haml +3 -1
- data/views/hardware_profiles/index.xml.haml +2 -2
- data/views/hardware_profiles/show.html.haml +3 -3
- data/views/hardware_profiles/show.xml.haml +3 -3
- data/views/images/show.html.haml +5 -0
- data/views/images/show.xml.haml +6 -1
- data/views/instance_states/show.html.haml +1 -1
- data/views/instances/index.html.haml +1 -1
- data/views/instances/new.html.haml +54 -39
- data/views/instances/run_command.html.haml +24 -15
- data/views/instances/show.html.haml +7 -3
- data/views/instances/show.xml.haml +2 -2
- data/views/keys/show.xml.haml +1 -0
- data/views/layout.html.haml +5 -9
- data/views/load_balancers/show.html.haml +12 -6
- data/views/metrics/index.html.haml +13 -0
- data/views/metrics/index.xml.haml +5 -0
- data/views/metrics/show.html.haml +23 -0
- data/views/metrics/show.xml.haml +9 -0
- data/views/realms/show.xml.haml +2 -1
- data/views/root/index.html.haml +1 -1
- data/views/storage_snapshots/show.html.haml +1 -1
- data/views/storage_snapshots/show.xml.haml +1 -0
- data/views/storage_volumes/attach.html.haml +1 -2
- data/views/storage_volumes/index.html.haml +1 -1
- data/views/storage_volumes/new.html.haml +22 -16
- data/views/storage_volumes/show.html.haml +10 -4
- data/views/storage_volumes/show.xml.haml +3 -4
- metadata +547 -519
- data/DISCLAIMER +0 -8
- data/lib/cimi/model.rb +0 -52
- data/lib/cimi/model/network.rb +0 -69
- data/lib/deltacloud/base_driver.rb +0 -18
- data/lib/deltacloud/base_driver/features.rb +0 -262
- data/lib/deltacloud/base_driver/mock_driver.rb +0 -78
- data/lib/deltacloud/drivers/ec2/ec2_mock_driver.rb +0 -186
- data/lib/deltacloud/drivers/rhevm/rhevm_client.rb +0 -521
- data/lib/deltacloud/helpers/application_helper.rb +0 -267
- data/lib/deltacloud/helpers/hardware_profiles_helper.rb +0 -50
- data/lib/deltacloud/helpers/json_helper.rb +0 -31
- data/lib/deltacloud/method_serializer.rb +0 -83
- data/lib/deltacloud/validation.rb +0 -107
- data/lib/sinatra/rabbit.rb +0 -441
- data/lib/sinatra/rack_runtime.rb +0 -47
- data/lib/sinatra/rack_syslog.rb +0 -86
- data/lib/sinatra/sinatra_verbose.rb +0 -73
- data/lib/sinatra/static_assets.rb +0 -99
- data/public/javascripts/jquery.mobile-1.0rc1.min.js +0 -170
- data/public/stylesheets/jquery.mobile-1.0rc1.min.css +0 -12
- data/tests/drivers/google/setup.rb +0 -38
- data/tests/drivers/mock/instance_states_test.rb +0 -71
- data/tests/drivers/mock/setup.rb +0 -3
- data/tests/drivers/mock/url_for_test.rb +0 -67
- data/tests/drivers/openstack/setup.rb +0 -20
- data/views/cimi/errors/400.html.haml +0 -41
- data/views/cimi/errors/401.html.haml +0 -41
- data/views/cimi/errors/404.html.haml +0 -29
- data/views/cimi/errors/405.html.haml +0 -29
- data/views/cimi/errors/502.html.haml +0 -43
- 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
|