bosh_cpi 2.0.0 → 2.0.1

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 99a4eed7d57155aaab71e8d3d96ba913ca1c3dd7
4
- data.tar.gz: 989cf2cf09fb1a47addb39348441be1d9f10fb87
3
+ metadata.gz: 77f9ed40e596e15952e7c43452f19682c0e1ea83
4
+ data.tar.gz: 9939c9c1db5797ee1311541d2e8d34ab63dc6d14
5
5
  SHA512:
6
- metadata.gz: dc58193908a9b31b1a0eb6d03d6f6a93e8b9fa95c318691d017a5d9853d264c0c37577333c6ab052a54681861507a46a6e8da9cfc29c46a98a524f0e4e73e57f
7
- data.tar.gz: f415c989376f1cbd0eac4388a7da3ec57964af26467e7d84da3d3217fdf184a4e103d6e3b0a7bb1034a28cb80effa9fa77bfda7fb27d6290426bb97406070cba
6
+ metadata.gz: c889201fd88c6abbbb1cb77b4fc123dc1f49398dff060907f5ed1e9d9c79e6999fd2f94aebeb0a644fcfaeb9cebc094d67557791ded96b03ef358a05f5b4f086
7
+ data.tar.gz: 98d595503bd95bb1e477bd7ba47529f675e34d7d574e152276ba9c9147595c629cef76567819a763b0abe7659a5b873da0a6dd3706d5c3d89569eb40e9f43318
@@ -0,0 +1,129 @@
1
+ require 'json'
2
+
3
+ class Bosh::Cpi::Cli
4
+ KNOWN_RPC_METHODS = %w(
5
+ current_vm_id
6
+ create_stemcell
7
+ delete_stemcell
8
+ create_vm
9
+ delete_vm
10
+ has_vm
11
+ reboot_vm
12
+ set_vm_metadata
13
+ create_disk
14
+ has_disk
15
+ delete_disk
16
+ attach_disk
17
+ detach_disk
18
+ snapshot_disk
19
+ delete_snapshot
20
+ get_disks
21
+ ping
22
+ calculate_vm_cloud_properties
23
+ ).freeze
24
+
25
+ RPC_METHOD_TO_RUBY_METHOD = {
26
+ 'has_vm' => 'has_vm?',
27
+ 'has_disk' => 'has_disk?',
28
+ }.freeze
29
+
30
+ INVALID_CALL_ERROR_TYPE = 'InvalidCall'.freeze
31
+ UNKNOWN_ERROR_TYPE = 'Unknown'.freeze
32
+
33
+ def initialize(cpi, logs_string_io, result_io)
34
+ @cpi = cpi
35
+ @logs_string_io = logs_string_io
36
+ @result_io = result_io
37
+ end
38
+
39
+ def run(json)
40
+ begin
41
+ request = JSON.load(json)
42
+ rescue JSON::ParserError => e
43
+ return error_response(INVALID_CALL_ERROR_TYPE, "Request cannot be deserialized, details: #{e.message}", false, e.backtrace)
44
+ end
45
+
46
+ method = request['method']
47
+ unless method.is_a?(String)
48
+ return error_response(INVALID_CALL_ERROR_TYPE, "Method must be a String, got: '#{method.inspect}'", false)
49
+ end
50
+
51
+ unless KNOWN_RPC_METHODS.include?(method)
52
+ return error_response(INVALID_CALL_ERROR_TYPE, "Method is not known, got: '#{method}'", false)
53
+ end
54
+
55
+ arguments = request['arguments']
56
+ unless arguments.is_a?(Array)
57
+ return error_response(INVALID_CALL_ERROR_TYPE, "Arguments must be an Array, got: '#{arguments.inspect}'", false)
58
+ end
59
+
60
+ context = request['context']
61
+ unless context.is_a?(Hash) && context['director_uuid'].is_a?(String)
62
+ return error_response(INVALID_CALL_ERROR_TYPE, "Request should include context with director uuid, got: '#{context.inspect}'", false)
63
+ end
64
+
65
+ configure_director(context['director_uuid'])
66
+
67
+ ruby_method = RPC_METHOD_TO_RUBY_METHOD[method] || method
68
+
69
+ begin
70
+ cpi = @cpi.call(context)
71
+ result = cpi.public_send(ruby_method, *arguments)
72
+ rescue Bosh::Clouds::RetriableCloudError => e
73
+ return error_response(error_name(e), e.message, e.ok_to_retry, e.backtrace)
74
+ rescue Bosh::Clouds::CloudError, Bosh::Clouds::CpiError => e
75
+ return error_response(error_name(e), e.message, false, e.backtrace)
76
+ rescue ArgumentError => e
77
+ return error_response(INVALID_CALL_ERROR_TYPE, "Arguments are not correct, details: '#{e.message}'", false, e.backtrace)
78
+ rescue Exception => e
79
+ return error_response(UNKNOWN_ERROR_TYPE, e.message, false, e.backtrace)
80
+ end
81
+
82
+ result_response(result)
83
+ end
84
+
85
+ private
86
+
87
+ def configure_director(director_uuid)
88
+ Bosh::Clouds::Config.uuid = director_uuid
89
+ end
90
+
91
+ def error_response(type, message, ok_to_retry, bt=[])
92
+ if !bt.empty?
93
+ @logs_string_io.print("Rescued #{type}: #{message}. backtrace: #{bt.join("\n")}")
94
+ end
95
+
96
+ hash = {
97
+ result: nil,
98
+ error: {
99
+ type: type,
100
+ message: message,
101
+ ok_to_retry: ok_to_retry,
102
+ },
103
+ log: encode_string_as_utf8(@logs_string_io.string)
104
+ }
105
+ @result_io.print(JSON.dump(hash)); nil
106
+ end
107
+
108
+ def result_response(result)
109
+ hash = {
110
+ result: result,
111
+ error: nil,
112
+ log: encode_string_as_utf8(@logs_string_io.string)
113
+ }
114
+ @result_io.print(JSON.dump(hash)); nil
115
+ end
116
+
117
+ def error_name(error)
118
+ error.class.name
119
+ end
120
+
121
+ def encode_string_as_utf8(src)
122
+ log = @logs_string_io.string.force_encoding(Encoding::UTF_8)
123
+ unless log.valid_encoding?
124
+ # the src encoding hint of Encoding::BINARY is only required for ruby 1.9.3
125
+ log = @logs_string_io.string.encode(Encoding::UTF_8, Encoding::BINARY, undef: :replace, invalid: :replace)
126
+ end
127
+ log
128
+ end
129
+ end
@@ -0,0 +1,15 @@
1
+ require 'bosh/cpi/compatibility_helpers'
2
+
3
+ module Bosh::Cpi::CompatibilityHelpers
4
+ def it_can_delete_non_existent_vm(vm_cid='vm-cid')
5
+ describe "delete_vm (deleting non existent vm)" do
6
+ context "when VM is not present" do
7
+ it "raises VMNotFound error" do
8
+ expect {
9
+ cpi.delete_vm(vm_cid)
10
+ }.to raise_error(Bosh::Clouds::VMNotFound, "VM '#{vm_cid}' not found")
11
+ end
12
+ end
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,5 @@
1
+ require 'bosh/cpi'
2
+
3
+ module Bosh::Cpi
4
+ module CompatibilityHelpers; end
5
+ end
@@ -0,0 +1,106 @@
1
+ require 'cloud/errors'
2
+ require 'httpclient'
3
+ require 'base64'
4
+ require 'json'
5
+
6
+ module Bosh::Cpi
7
+ class RegistryClient
8
+ attr_reader :endpoint
9
+ attr_reader :user
10
+ attr_reader :password
11
+
12
+ def initialize(endpoint, user, password)
13
+ @endpoint = endpoint
14
+
15
+ unless @endpoint =~ /^http:\/\//
16
+ @endpoint = "http://#{@endpoint}"
17
+ end
18
+
19
+ @user = user
20
+ @password = password
21
+
22
+ auth = Base64.encode64("#{@user}:#{@password}").gsub("\n", '')
23
+
24
+ @headers = {
25
+ "Accept" => 'application/json',
26
+ "Authorization" => "Basic #{auth}"
27
+ }
28
+
29
+ @client = HTTPClient.new
30
+ end
31
+
32
+ ##
33
+ # Update instance settings in the registry
34
+ # @param [String] instance_id EC2 instance id
35
+ # @param [Hash] settings New agent settings
36
+ # @return [Boolean]
37
+ def update_settings(instance_id, settings)
38
+ unless settings.is_a?(Hash)
39
+ raise ArgumentError, "Invalid settings format, Hash expected, #{settings.class} given"
40
+ end
41
+
42
+ payload = JSON.dump(settings)
43
+ url = "#{@endpoint}/instances/#{instance_id}/settings"
44
+
45
+ response = @client.put(url, {:body => payload, :header => @headers})
46
+
47
+ unless HTTP::Status.successful?(response.status)
48
+ cloud_error("Cannot update settings for '#{instance_id}', got HTTP #{response.status}")
49
+ end
50
+
51
+ true
52
+ end
53
+
54
+ ##
55
+ # Read instance settings from the registry
56
+ # @param [String] instance_id EC2 instance id
57
+ # @return [Hash] Agent settings
58
+ def read_settings(instance_id)
59
+ url = "#{@endpoint}/instances/#{instance_id}/settings"
60
+
61
+ response = @client.get(url, {:header => @headers})
62
+
63
+ if response.status != 200
64
+ cloud_error("Cannot read settings for '#{instance_id}', got HTTP #{response.status}")
65
+ end
66
+
67
+ body = JSON.load(response.body)
68
+
69
+ unless body.is_a?(Hash)
70
+ cloud_error("Invalid registry response, Hash expected, got #{body.class}: #{body}")
71
+ end
72
+
73
+ settings = JSON.load(body["settings"])
74
+
75
+ unless settings.is_a?(Hash)
76
+ cloud_error("Invalid settings format, Hash expected, got #{settings.class}: #{settings}")
77
+ end
78
+
79
+ settings
80
+ rescue JSON::ParserError => e
81
+ cloud_error("Cannot parse settings for '#{instance_id}': #{e.message}")
82
+ end
83
+
84
+ ##
85
+ # Delete instance settings from the registry
86
+ # @param [String] instance_id EC2 instance id
87
+ # @return [Boolean]
88
+ def delete_settings(instance_id)
89
+ url = "#{@endpoint}/instances/#{instance_id}/settings"
90
+
91
+ response = @client.delete(url, {:header => @headers})
92
+
93
+ unless [200, 404].include? response.status
94
+ cloud_error("Cannot delete settings for '#{instance_id}', got HTTP #{response.status}")
95
+ end
96
+
97
+ true
98
+ end
99
+
100
+ private
101
+
102
+ def cloud_error(message)
103
+ raise Bosh::Clouds::CloudError, message
104
+ end
105
+ end
106
+ end
@@ -0,0 +1,10 @@
1
+ require 'rspec'
2
+ require 'rspec/core/rake_task'
3
+
4
+ namespace :spec do
5
+ desc 'Run VM lifecycle specs'
6
+ RSpec::Core::RakeTask.new(:lifecycle) do |t|
7
+ t.pattern = 'spec/integration'
8
+ t.rspec_opts = %w(--format documentation --color)
9
+ end
10
+ end
@@ -0,0 +1 @@
1
+ Dir.glob(File.expand_path('tasks/**/*.rake', File.dirname(__FILE__))).each { |r| import r }
data/lib/bosh/cpi.rb ADDED
@@ -0,0 +1,6 @@
1
+ module Bosh
2
+ module Cpi; end
3
+ end
4
+
5
+ require 'bosh/cpi/cli'
6
+ require 'bosh/cpi/registry_client'
@@ -0,0 +1,17 @@
1
+ require 'forwardable'
2
+
3
+ module Bosh::Clouds
4
+ class Config
5
+
6
+ class << self
7
+ extend Forwardable
8
+ def_delegators :@delegate, :db, :logger, :uuid, :uuid=, :task_checkpoint, :cpi_task_log
9
+ end
10
+
11
+ # @param [Bosh::Director::Config] config director config file
12
+ def self.configure(config)
13
+ @delegate = config
14
+ end
15
+
16
+ end
17
+ end
@@ -0,0 +1,21 @@
1
+ module Bosh::Clouds
2
+ class CpiError < StandardError; end
3
+ class NotImplemented < CpiError; end
4
+ class NotSupported < CpiError; end
5
+
6
+ class CloudError < StandardError; end
7
+ class VMNotFound < CloudError; end
8
+
9
+ class RetriableCloudError < CloudError
10
+ attr_accessor :ok_to_retry
11
+
12
+ def initialize(ok_to_retry)
13
+ @ok_to_retry = ok_to_retry
14
+ end
15
+ end
16
+
17
+ class NoDiskSpace < RetriableCloudError; end
18
+ class DiskNotAttached < RetriableCloudError; end
19
+ class DiskNotFound < RetriableCloudError; end
20
+ class VMCreationFailed < RetriableCloudError; end
21
+ end
@@ -0,0 +1,163 @@
1
+ require 'membrane'
2
+ require 'open3'
3
+
4
+ module Bosh::Clouds
5
+ class ExternalCpi
6
+ # Raised when the external CPI executable returns an error unknown to director
7
+ class UnknownError < StandardError; end
8
+
9
+ # Raised when the external CPI executable returns nil or invalid JSON format to director
10
+ class InvalidResponse < StandardError; end
11
+
12
+ # Raised when the external CPI bin/cpi is not executable
13
+ class NonExecutable < StandardError; end
14
+
15
+ KNOWN_RPC_ERRORS = %w(
16
+ Bosh::Clouds::CpiError
17
+ Bosh::Clouds::NotSupported
18
+ Bosh::Clouds::NotImplemented
19
+
20
+ Bosh::Clouds::CloudError
21
+ Bosh::Clouds::VMNotFound
22
+
23
+ Bosh::Clouds::NoDiskSpace
24
+ Bosh::Clouds::DiskNotAttached
25
+ Bosh::Clouds::DiskNotFound
26
+ Bosh::Clouds::VMCreationFailed
27
+ ).freeze
28
+
29
+ RESPONSE_SCHEMA = Membrane::SchemaParser.parse do
30
+ {
31
+ 'result' => any,
32
+ 'error' => enum(nil,
33
+ { 'type' => String,
34
+ 'message' => String,
35
+ 'ok_to_retry' => bool
36
+ }
37
+ ),
38
+ 'log' => String
39
+ }
40
+ end
41
+
42
+ def initialize(cpi_path, director_uuid, properties_from_cpi_config = nil)
43
+ @cpi_path = cpi_path
44
+ @director_uuid = director_uuid
45
+ @logger = Config.logger
46
+ @properties_from_cpi_config = properties_from_cpi_config
47
+ end
48
+
49
+ def current_vm_id(*arguments); invoke_cpi_method(__method__.to_s, *arguments); end
50
+ def create_stemcell(*arguments); invoke_cpi_method(__method__.to_s, *arguments); end
51
+ def delete_stemcell(*arguments); invoke_cpi_method(__method__.to_s, *arguments); end
52
+ def create_vm(*arguments) invoke_cpi_method(__method__.to_s, *arguments); end
53
+ def delete_vm(*arguments); invoke_cpi_method(__method__.to_s, *arguments); end
54
+ def has_vm?(*arguments); invoke_cpi_method(__method__.to_s, *arguments); end
55
+ def reboot_vm(*arguments); invoke_cpi_method(__method__.to_s, *arguments); end
56
+ def set_vm_metadata(*arguments); invoke_cpi_method(__method__.to_s, *arguments); end
57
+ def create_disk(*arguments); invoke_cpi_method(__method__.to_s, *arguments); end
58
+ def has_disk?(*arguments); invoke_cpi_method(__method__.to_s, *arguments); end
59
+ def delete_disk(*arguments); invoke_cpi_method(__method__.to_s, *arguments); end
60
+ def attach_disk(*arguments); invoke_cpi_method(__method__.to_s, *arguments); end
61
+ def detach_disk(*arguments); invoke_cpi_method(__method__.to_s, *arguments); end
62
+ def snapshot_disk(*arguments); invoke_cpi_method(__method__.to_s, *arguments); end
63
+ def delete_snapshot(*arguments); invoke_cpi_method(__method__.to_s, *arguments); end
64
+ def get_disks(*arguments); invoke_cpi_method(__method__.to_s, *arguments); end
65
+ def ping(*arguments); invoke_cpi_method(__method__.to_s, *arguments); end
66
+
67
+ private
68
+
69
+ def invoke_cpi_method(method_name, *arguments)
70
+ context = {
71
+ 'director_uuid' => @director_uuid
72
+ }
73
+ context.merge!(@properties_from_cpi_config) unless @properties_from_cpi_config.nil?
74
+
75
+ request = request_json(method_name, arguments, context)
76
+ redacted_request = request_json(method_name, arguments, redact_context(context))
77
+
78
+ env = {'PATH' => '/usr/sbin:/usr/bin:/sbin:/bin', 'TMPDIR' => ENV['TMPDIR']}
79
+ cpi_exec_path = checked_cpi_exec_path
80
+
81
+ @logger.debug("External CPI sending request: #{redacted_request} with command: #{cpi_exec_path}")
82
+ cpi_response, stderr, exit_status = Open3.capture3(env, cpi_exec_path, stdin_data: request, unsetenv_others: true)
83
+ @logger.debug("External CPI got response: #{cpi_response}, err: #{stderr}, exit_status: #{exit_status}")
84
+
85
+ parsed_response = parsed_response(cpi_response)
86
+ validate_response(parsed_response)
87
+
88
+ if parsed_response['error']
89
+ handle_error(parsed_response['error'], method_name)
90
+ end
91
+
92
+ save_cpi_log(parsed_response['log'])
93
+ save_cpi_log(stderr)
94
+
95
+ parsed_response['result']
96
+ end
97
+
98
+ def checked_cpi_exec_path
99
+ unless File.executable?(@cpi_path)
100
+ raise NonExecutable, "Failed to run cpi: '#{@cpi_path}' is not executable"
101
+ end
102
+ @cpi_path
103
+ end
104
+
105
+ def redact_context(context)
106
+ return context if @properties_from_cpi_config.nil?
107
+ Hash[context.map{|k,v|[k,@properties_from_cpi_config.keys.include?(k) ? '<redacted>' : v]}]
108
+ end
109
+
110
+ def request_json(method_name, arguments, context)
111
+ JSON.dump({
112
+ 'method' => method_name.gsub(/\?$/,''),
113
+ 'arguments' => arguments,
114
+ 'context' => context
115
+ })
116
+ end
117
+
118
+ def handle_error(error_response, method_name)
119
+ error_type = error_response['type']
120
+ error_message = error_response['message']
121
+ unless KNOWN_RPC_ERRORS.include?(error_type)
122
+ raise UnknownError, "Unknown CPI error '#{error_type}' with message '#{error_message}' in '#{method_name}' CPI method"
123
+ end
124
+
125
+ error_class = constantize(error_type)
126
+
127
+ if error_class <= RetriableCloudError
128
+ error = error_class.new(error_response['ok_to_retry'])
129
+ else
130
+ error = error_class.new(error_message)
131
+ end
132
+
133
+ raise error, "CPI error '#{error_type}' with message '#{error_message}' in '#{method_name}' CPI method"
134
+ end
135
+
136
+ def save_cpi_log(output)
137
+ # cpi log path is set up at the beginning of every task in Config
138
+ # see JobRunner#setup_task_logging
139
+ File.open(Config.cpi_task_log, 'a') do |f|
140
+ f.write(output)
141
+ end
142
+ end
143
+
144
+ def parsed_response(input)
145
+ begin
146
+ JSON.load(input)
147
+ rescue JSON::ParserError => e
148
+ raise InvalidResponse, "Invalid CPI response - ParserError - #{e.message}"
149
+ end
150
+ end
151
+
152
+ def validate_response(response)
153
+ RESPONSE_SCHEMA.validate(response)
154
+ rescue Membrane::SchemaValidationError => e
155
+ raise InvalidResponse, "Invalid CPI response - SchemaValidationError: #{e.message}"
156
+ end
157
+
158
+ def constantize(camel_cased_word)
159
+ error_name = camel_cased_word.split('::').last
160
+ Bosh::Clouds.const_get(error_name)
161
+ end
162
+ end
163
+ end
@@ -0,0 +1,25 @@
1
+ module Bosh::Clouds
2
+ class InternalCpi
3
+ def initialize(cloud)
4
+ @cloud = cloud
5
+ end
6
+
7
+ private
8
+
9
+ def method_missing(method_sym, *arguments, &block)
10
+ invoke_cpi_method(method_sym, arguments)
11
+ end
12
+
13
+ def respond_to?(method_sym)
14
+ cloud.respond_to?(method_sym)
15
+ end
16
+
17
+ def cloud
18
+ @cloud
19
+ end
20
+
21
+ def invoke_cpi_method(method_sym, arguments)
22
+ cloud.send(method_sym, *JSON.parse(JSON.dump(arguments)))
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,31 @@
1
+ module Bosh::Clouds
2
+ class Provider
3
+ def self.create(cloud_config, director_uuid)
4
+ if cloud_config.has_key?('provider')
5
+ ExternalCpiProvider.create(cloud_config['provider']['path'], director_uuid)
6
+ else
7
+ InternalCpiProvider.create(cloud_config['plugin'], cloud_config['properties'])
8
+ end
9
+ end
10
+ end
11
+
12
+ private
13
+
14
+ class InternalCpiProvider
15
+ def self.create(plugin, options)
16
+ begin
17
+ require "cloud/#{plugin}"
18
+ rescue LoadError => error
19
+ raise CloudError, "Could not load Cloud Provider Plugin: #{plugin}, with error #{error.inspect}"
20
+ end
21
+
22
+ InternalCpi.new(Bosh::Clouds.const_get(plugin.capitalize).new(options))
23
+ end
24
+ end
25
+
26
+ class ExternalCpiProvider
27
+ def self.create(cpi_job_path, director_uuid)
28
+ ExternalCpi.new(cpi_job_path, director_uuid)
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,5 @@
1
+ module Bosh
2
+ module Clouds
3
+ VERSION = '2.0.1'
4
+ end
5
+ end
data/lib/cloud.rb ADDED
@@ -0,0 +1,229 @@
1
+ # Copyright (c) 2009-2012 VMware, Inc.
2
+
3
+ module Bosh; module Clouds; end; end
4
+
5
+ require "forwardable"
6
+
7
+ require "cloud/config"
8
+ require "cloud/errors"
9
+ require "cloud/provider"
10
+ require "cloud/external_cpi"
11
+ require "cloud/internal_cpi"
12
+
13
+ module Bosh
14
+
15
+ ##
16
+ # CPI - Cloud Provider Interface, used for interfacing with various IaaS APIs.
17
+ #
18
+ # Key terms:
19
+ # Stemcell: template used for creating VMs (shouldn't be powered on)
20
+ # VM: VM created from a stemcell with custom settings (networking and resources)
21
+ # Disk: volume that can be attached and detached from the VMs,
22
+ # never attached to more than a single VM at one time
23
+ class Cloud
24
+
25
+ ##
26
+ # Cloud initialization
27
+ #
28
+ # @param [Hash] options cloud options
29
+ def initialize(options)
30
+ end
31
+
32
+ ##
33
+ # Get the vm_id of this host
34
+ #
35
+ # @return [String] opaque id later used by other methods of the CPI
36
+ def current_vm_id
37
+ not_implemented(:current_vm_id)
38
+ end
39
+
40
+ ##
41
+ # Creates a stemcell
42
+ #
43
+ # @param [String] image_path path to an opaque blob containing the stemcell image
44
+ # @param [Hash] cloud_properties properties required for creating this template
45
+ # specific to a CPI
46
+ # @return [String] opaque id later used by {#create_vm} and {#delete_stemcell}
47
+ def create_stemcell(image_path, cloud_properties)
48
+ not_implemented(:create_stemcell)
49
+ end
50
+
51
+ ##
52
+ # Deletes a stemcell
53
+ #
54
+ # @param [String] stemcell stemcell id that was once returned by {#create_stemcell}
55
+ # @return [void]
56
+ def delete_stemcell(stemcell_id)
57
+ not_implemented(:delete_stemcell)
58
+ end
59
+
60
+ ##
61
+ # Creates a VM - creates (and powers on) a VM from a stemcell with the proper resources
62
+ # and on the specified network. When disk locality is present the VM will be placed near
63
+ # the provided disk so it won't have to move when the disk is attached later.
64
+ #
65
+ # Sample networking config:
66
+ # {"network_a" =>
67
+ # {
68
+ # "netmask" => "255.255.248.0",
69
+ # "ip" => "172.30.41.40",
70
+ # "gateway" => "172.30.40.1",
71
+ # "dns" => ["172.30.22.153", "172.30.22.154"],
72
+ # "cloud_properties" => {"name" => "VLAN444"}
73
+ # }
74
+ # }
75
+ #
76
+ # Sample resource pool config (CPI specific):
77
+ # {
78
+ # "ram" => 512,
79
+ # "disk" => 512,
80
+ # "cpu" => 1
81
+ # }
82
+ # or similar for EC2:
83
+ # {"name" => "m1.small"}
84
+ #
85
+ # @param [String] agent_id UUID for the agent that will be used later on by the director
86
+ # to locate and talk to the agent
87
+ # @param [String] stemcell stemcell id that was once returned by {#create_stemcell}
88
+ # @param [Hash] resource_pool cloud specific properties describing the resources needed
89
+ # for this VM
90
+ # @param [Hash] networks list of networks and their settings needed for this VM
91
+ # @param [String, Array] disk_locality disk id(s) if known of the disk(s) that will be
92
+ # attached to this vm
93
+ # @param [Hash] env environment that will be passed to this vm
94
+ # @return [String] opaque id later used by {#attach_disk}, {#detach_disk} and {#delete_vm}
95
+ def create_vm(agent_id, stemcell_id, resource_pool,
96
+ networks, disk_locality, env)
97
+ not_implemented(:create_vm)
98
+ end
99
+
100
+ ##
101
+ # Deletes a VM. If the VM has already been deleted, this call returns normally and has no effect.
102
+ #
103
+ # @param [String] vm vm id that was once returned by {#create_vm}
104
+ # @return [void]
105
+ def delete_vm(vm_id)
106
+ not_implemented(:delete_vm)
107
+ end
108
+
109
+ ##
110
+ # Checks if a VM exists
111
+ #
112
+ # @param [String] vm vm id that was once returned by {#create_vm}
113
+ # @return [Boolean] True if the vm exists
114
+ def has_vm?(vm_id)
115
+ not_implemented(:has_vm?)
116
+ end
117
+
118
+ ##
119
+ # Checks if a disk exists
120
+ #
121
+ # @param [String] disk disk_id that was once returned by {#create_disk}
122
+ # @return [Boolean] True if the disk exists
123
+ def has_disk?(disk_id)
124
+ not_implemented(:has_disk?)
125
+ end
126
+
127
+ ##
128
+ # Reboots a VM
129
+ #
130
+ # @param [String] vm vm id that was once returned by {#create_vm}
131
+ # @param [Optional, Hash] CPI specific options (e.g hard/soft reboot)
132
+ # @return [void]
133
+ def reboot_vm(vm_id)
134
+ not_implemented(:reboot_vm)
135
+ end
136
+
137
+ ##
138
+ # Set metadata for a VM
139
+ #
140
+ # Optional. Implement to provide more information for the IaaS.
141
+ #
142
+ # @param [String] vm vm id that was once returned by {#create_vm}
143
+ # @param [Hash] metadata metadata key/value pairs
144
+ # @return [void]
145
+ def set_vm_metadata(vm, metadata)
146
+ not_implemented(:set_vm_metadata)
147
+ end
148
+
149
+ ##
150
+ # Creates a disk (possibly lazily) that will be attached later to a VM. When
151
+ # VM locality is specified the disk will be placed near the VM so it won't have to move
152
+ # when it's attached later.
153
+ #
154
+ # @param [Integer] size disk size in MB
155
+ # @param [Hash] cloud_properties properties required for creating this disk
156
+ # specific to a CPI
157
+ # @param [String] vm_locality vm id if known of the VM that this disk will
158
+ # be attached to
159
+ # @return [String] opaque id later used by {#attach_disk}, {#detach_disk}, and {#delete_disk}
160
+ def create_disk(size, cloud_properties, vm_locality)
161
+ not_implemented(:create_disk)
162
+ end
163
+
164
+ ##
165
+ # Deletes a disk
166
+ # Will raise an exception if the disk is attached to a VM
167
+ #
168
+ # @param [String] disk disk id that was once returned by {#create_disk}
169
+ # @return [void]
170
+ def delete_disk(disk_id)
171
+ not_implemented(:delete_disk)
172
+ end
173
+
174
+ # Attaches a disk
175
+ # @param [String] vm vm id that was once returned by {#create_vm}
176
+ # @param [String] disk disk id that was once returned by {#create_disk}
177
+ # @return [void]
178
+ def attach_disk(vm_id, disk_id)
179
+ not_implemented(:attach_disk)
180
+ end
181
+
182
+ # Take snapshot of disk
183
+ # @param [String] disk_id disk id of the disk to take the snapshot of
184
+ # @param [Hash] metadata metadata key/value pairs
185
+ # @return [String] snapshot id
186
+ def snapshot_disk(disk_id, metadata)
187
+ not_implemented(:snapshot_disk)
188
+ end
189
+
190
+ # Delete a disk snapshot
191
+ # @param [String] snapshot_id snapshot id to delete
192
+ # @return [void]
193
+ def delete_snapshot(snapshot_id)
194
+ not_implemented(:delete_snapshot)
195
+ end
196
+
197
+ # Detaches a disk
198
+ # @param [String] vm vm id that was once returned by {#create_vm}
199
+ # @param [String] disk disk id that was once returned by {#create_disk}
200
+ # @return [void]
201
+ def detach_disk(vm_id, disk_id)
202
+ not_implemented(:detach_disk)
203
+ end
204
+
205
+ # List the attached disks of the VM.
206
+ # @param [String] vm_id is the CPI-standard vm_id (eg, returned from current_vm_id)
207
+ # @return [array[String]] list of opaque disk_ids that can be used with the
208
+ # other disk-related methods on the CPI
209
+ def get_disks(vm_id)
210
+ not_implemented(:get_disks)
211
+ end
212
+
213
+ # Specify VM's hardware resources
214
+ # @param [Hash] vm_properties (typically cpu, ram, ephemeral_disk_size)
215
+ # @return [Hash] opaque description of the VM's configuration that
216
+ # can be used with {#create_vm}
217
+ def calculate_vm_cloud_properties(vm_properties)
218
+ not_implemented(:calculate_vm_cloud_properties)
219
+ end
220
+
221
+ private
222
+
223
+ def not_implemented(method)
224
+ raise Bosh::Clouds::NotImplemented,
225
+ "'#{method}' is not implemented by #{self.class}"
226
+ end
227
+
228
+ end
229
+ end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: bosh_cpi
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.0.0
4
+ version: 2.0.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - VMware
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2016-11-10 00:00:00.000000000 Z
11
+ date: 2016-11-11 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bosh_common
@@ -57,7 +57,21 @@ email: support@cloudfoundry.com
57
57
  executables: []
58
58
  extensions: []
59
59
  extra_rdoc_files: []
60
- files: []
60
+ files:
61
+ - lib/bosh/cpi.rb
62
+ - lib/bosh/cpi/cli.rb
63
+ - lib/bosh/cpi/compatibility_helpers.rb
64
+ - lib/bosh/cpi/compatibility_helpers/delete_vm.rb
65
+ - lib/bosh/cpi/registry_client.rb
66
+ - lib/bosh/cpi/tasks.rb
67
+ - lib/bosh/cpi/tasks/spec.rake
68
+ - lib/cloud.rb
69
+ - lib/cloud/config.rb
70
+ - lib/cloud/errors.rb
71
+ - lib/cloud/external_cpi.rb
72
+ - lib/cloud/internal_cpi.rb
73
+ - lib/cloud/provider.rb
74
+ - lib/cloud/version.rb
61
75
  homepage: https://github.com/cloudfoundry/bosh
62
76
  licenses:
63
77
  - Apache 2.0