class Bosh::Clouds::Dummy
Constants
- ATTACH_DISK_SCHEMA
- CREATE_DISK_SCHEMA
- CREATE_STEMCELL_SCHEMA
- CREATE_VM_SCHEMA
- DELETE_SNAPSHOT_SCHEMA
- DELETE_STEMCELL_SCHEMA
- DELETE_VM_SCHEMA
- DELTE_DISK_SCHEMA
- DETACH_DISK_SCHEMA
- HAS_DISK_SCHEMA
- HAS_VM_SCHEMA
- REBOOT_VM_SCHEMA
- SET_VM_METADATA_SCHEMA
- SNAPSHOT_DISK_SCHEMA
Attributes
commands[R]
Public Class Methods
new(options)
click to toggle source
# File lib/cloud/dummy.rb, line 13 def initialize(options) @options = options @base_dir = options['dir'] if @base_dir.nil? raise ArgumentError, 'Must specify dir' end @running_vms_dir = File.join(@base_dir, 'running_vms') @vm_repo = VMRepo.new(@running_vms_dir) @tmp_dir = File.join(@base_dir, 'tmp') FileUtils.mkdir_p(@tmp_dir) @logger = Logging::Logger.new('DummyCPI') @logger.add_appenders(Logging.appenders.io( 'DummyCPIIO', options['log_buffer'] || STDOUT )) @commands = CommandTransport.new(@base_dir, @logger) @inputs_recorder = InputsRecorder.new(@base_dir, @logger) prepare rescue Errno::EACCES raise ArgumentError, "cannot create dummy cloud base directory #{@base_dir}" end
Public Instance Methods
agent_dir_for_vm_cid(vm_cid)
click to toggle source
# File lib/cloud/dummy.rb, line 307 def agent_dir_for_vm_cid(vm_cid) agent_id = agent_id_for_vm_id(vm_cid) agent_base_dir(agent_id) end
agent_log_path(agent_id)
click to toggle source
# File lib/cloud/dummy.rb, line 259 def agent_log_path(agent_id) "#{@base_dir}/agent.#{agent_id}.log" end
all_ips()
click to toggle source
# File lib/cloud/dummy.rb, line 301 def all_ips Dir.glob(File.join(@base_dir, 'dummy_cpi_networks', '*')) .reject { |path| File.directory?(path) } .map { |path| File.basename(path) } end
all_snapshots()
click to toggle source
# File lib/cloud/dummy.rb, line 293 def all_snapshots if File.exists?(snapshot_file('')) Dir.glob(snapshot_file('*')) else [] end end
all_stemcells()
click to toggle source
# File lib/cloud/dummy.rb, line 275 def all_stemcells files = Dir.entries(@base_dir).select { |file| file.match /stemcell_./ } Dir.chdir(@base_dir) do [].tap do |results| files.each do |file| # data --> [{ 'name' => 'ubuntu-stemcell', 'version': '1', 'image' => <image path> }] data = YAML.load(File.read(file)) results << { 'id' => file.sub(/^stemcell_/, '') }.merge(data) end end.sort { |a, b| a[:version].to_i <=> b[:version].to_i } end end
attach_disk(vm_cid, disk_id)
click to toggle source
# File lib/cloud/dummy.rb, line 159 def attach_disk(vm_cid, disk_id) validate_and_record_inputs(ATTACH_DISK_SCHEMA, __method__, vm_cid, disk_id) if disk_attached?(disk_id) raise "#{disk_id} is already attached to an instance" end file = attachment_file(vm_cid, disk_id) FileUtils.mkdir_p(File.dirname(file)) FileUtils.touch(file) @logger.info("Attached disk: '#{disk_id}' to vm: '#{vm_cid}' at attachment file: #{file}") agent_id = agent_id_for_vm_id(vm_cid) settings = read_agent_settings(agent_id) settings['disks']['persistent'][disk_id] = 'attached' write_agent_settings(agent_id, settings) end
create_disk(size, cloud_properties, vm_locality)
click to toggle source
# File lib/cloud/dummy.rb, line 191 def create_disk(size, cloud_properties, vm_locality) validate_and_record_inputs(CREATE_DISK_SCHEMA, __method__, size, cloud_properties, vm_locality) disk_id = SecureRandom.hex file = disk_file(disk_id) FileUtils.mkdir_p(File.dirname(file)) File.write(file, size.to_s) disk_id end
create_stemcell(image_path, cloud_properties)
click to toggle source
# File lib/cloud/dummy.rb, line 41 def create_stemcell(image_path, cloud_properties) validate_and_record_inputs(CREATE_STEMCELL_SCHEMA, __method__, image_path, cloud_properties) content = File.read(image_path) data = YAML.load(content) data.merge!('image' => image_path) stemcell_id = Digest::SHA1.hexdigest(content) File.write(stemcell_file(stemcell_id), YAML.dump(data)) stemcell_id end
create_vm(agent_id, stemcell_id, cloud_properties, networks, disk_cids, env)
click to toggle source
rubocop:disable ParameterLists
# File lib/cloud/dummy.rb, line 71 def create_vm(agent_id, stemcell_id, cloud_properties, networks, disk_cids, env) # rubocop:enable ParameterLists @logger.info('Dummy: create_vm') validate_and_record_inputs(CREATE_VM_SCHEMA, __method__, agent_id, stemcell_id, cloud_properties, networks, disk_cids, env) ips = [] cmd = commands.next_create_vm_cmd if cmd.failed? raise Bosh::Clouds::CloudError.new("Creating vm failed") end networks.each do |network_name, network| if network['type'] != 'dynamic' ips << { 'network' => network_name, 'ip' => network.fetch('ip') } else if cmd.ip_address ip_address = cmd.ip_address elsif cloud_properties['az_name'] ip_address = cmd.ip_address_for_az(cloud_properties['az_name']) else ip_address = NetAddr::CIDRv4.new(rand(0..4294967295)).ip #collisions? end if ip_address ips << { 'network' => network_name, 'ip' => ip_address } write_agent_default_network(agent_id, ip_address) end end end allocate_ips(ips) write_agent_settings(agent_id, { agent_id: agent_id, blobstore: @options['agent']['blobstore'], ntp: [], disks: { persistent: {} }, networks: networks, vm: { name: "vm-#{agent_id}" }, cert: '', env: env, mbus: @options['nats'], }) agent_pid = spawn_agent_process(agent_id) vm = VM.new(agent_pid.to_s, agent_id, cloud_properties, ips) @vm_repo.save(vm) vm.id end
current_apply_spec_for_vm(vm_cid)
click to toggle source
# File lib/cloud/dummy.rb, line 316 def current_apply_spec_for_vm(vm_cid) agent_base_dir = agent_dir_for_vm_cid(vm_cid) spec_file = File.join(agent_base_dir, 'bosh', 'spec.json') JSON.parse(File.read(spec_file)) end
delete_disk(disk_id)
click to toggle source
# File lib/cloud/dummy.rb, line 201 def delete_disk(disk_id) validate_and_record_inputs(DELTE_DISK_SCHEMA, __method__, disk_id) FileUtils.rm(disk_file(disk_id)) end
delete_snapshot(snapshot_id)
click to toggle source
# File lib/cloud/dummy.rb, line 217 def delete_snapshot(snapshot_id) validate_and_record_inputs(DELETE_SNAPSHOT_SCHEMA, __method__, snapshot_id) FileUtils.rm(snapshot_file(snapshot_id)) end
delete_stemcell(stemcell_id)
click to toggle source
# File lib/cloud/dummy.rb, line 54 def delete_stemcell(stemcell_id) validate_and_record_inputs(DELETE_STEMCELL_SCHEMA, __method__, stemcell_id) FileUtils.rm(stemcell_file(stemcell_id)) end
delete_vm(vm_cid)
click to toggle source
# File lib/cloud/dummy.rb, line 125 def delete_vm(vm_cid) validate_and_record_inputs(DELETE_VM_SCHEMA, __method__, vm_cid) commands.wait_for_unpause_delete_vms detach_disks_attached_to_vm(vm_cid) agent_pid = vm_cid.to_i Process.kill('KILL', agent_pid) # rubocop:disable HandleExceptions rescue Errno::ESRCH # rubocop:enable HandleExceptions ensure free_ips(vm_cid) FileUtils.rm_rf(File.join(@base_dir, 'running_vms', vm_cid)) end
detach_disk(vm_cid, disk_id)
click to toggle source
# File lib/cloud/dummy.rb, line 177 def detach_disk(vm_cid, disk_id) validate_and_record_inputs(DETACH_DISK_SCHEMA, __method__, vm_cid, disk_id) unless disk_attached_to_vm?(vm_cid, disk_id) raise Bosh::Clouds::DiskNotAttached, "#{disk_id} is not attached to instance #{vm_cid}" end FileUtils.rm(attachment_file(vm_cid, disk_id)) agent_id = agent_id_for_vm_id(vm_cid) settings = read_agent_settings(agent_id) settings['disks']['persistent'].delete(disk_id) write_agent_settings(agent_id, settings) end
disk_attached_to_vm?(vm_cid, disk_id)
click to toggle source
# File lib/cloud/dummy.rb, line 312 def disk_attached_to_vm?(vm_cid, disk_id) File.exist?(attachment_file(vm_cid, disk_id)) end
disk_cids()
click to toggle source
# File lib/cloud/dummy.rb, line 243 def disk_cids # Shuffle so that no one relies on the order of disks Dir.glob(disk_file('*')).map { |disk| File.basename(disk) }.shuffle end
has_disk?(disk_id)
click to toggle source
# File lib/cloud/dummy.rb, line 153 def has_disk?(disk_id) validate_and_record_inputs(HAS_DISK_SCHEMA, __method__, disk_id) File.exists?(disk_file(disk_id)) end
has_vm?(vm_cid)
click to toggle source
# File lib/cloud/dummy.rb, line 147 def has_vm?(vm_cid) validate_and_record_inputs(HAS_VM_SCHEMA, __method__, vm_cid) @vm_repo.exists?(vm_cid) end
invocations()
click to toggle source
# File lib/cloud/dummy.rb, line 267 def invocations @inputs_recorder.read_all end
invocations_for_method(method)
click to toggle source
# File lib/cloud/dummy.rb, line 271 def invocations_for_method(method) @inputs_recorder.read(method) end
kill_agents()
click to toggle source
# File lib/cloud/dummy.rb, line 248 def kill_agents vm_cids.each do |agent_pid| begin Process.kill('KILL', agent_pid.to_i) # rubocop:disable HandleExceptions rescue Errno::ESRCH # rubocop:enable HandleExceptions end end end
latest_stemcell()
click to toggle source
# File lib/cloud/dummy.rb, line 289 def latest_stemcell all_stemcells.last end
prepare()
click to toggle source
Additional Dummy
test helpers
# File lib/cloud/dummy.rb, line 229 def prepare FileUtils.mkdir_p(@base_dir) end
read_cloud_properties(vm_cid)
click to toggle source
# File lib/cloud/dummy.rb, line 263 def read_cloud_properties(vm_cid) @vm_repo.load(vm_cid).cloud_properties end
reboot_vm(vm_cid)
click to toggle source
# File lib/cloud/dummy.rb, line 141 def reboot_vm(vm_cid) validate_and_record_inputs(__method__, vm_cid) raise NotImplemented, 'Dummy CPI does not implement reboot_vm' end
reset()
click to toggle source
# File lib/cloud/dummy.rb, line 233 def reset FileUtils.rm_rf(@base_dir) prepare end
set_vm_metadata(vm_cid, metadata)
click to toggle source
# File lib/cloud/dummy.rb, line 223 def set_vm_metadata(vm_cid, metadata) validate_and_record_inputs(SET_VM_METADATA_SCHEMA, __method__, vm_cid, metadata) end
snapshot_disk(disk_id, metadata)
click to toggle source
# File lib/cloud/dummy.rb, line 207 def snapshot_disk(disk_id, metadata) validate_and_record_inputs(SNAPSHOT_DISK_SCHEMA, __method__, disk_id, metadata) snapshot_id = SecureRandom.hex file = snapshot_file(snapshot_id) FileUtils.mkdir_p(File.dirname(file)) File.write(file, metadata.to_json) snapshot_id end
vm_cids()
click to toggle source
# File lib/cloud/dummy.rb, line 238 def vm_cids # Shuffle so that no one relies on the order of VMs Dir.glob(File.join(@running_vms_dir, '*')).map { |vm| File.basename(vm) }.shuffle end
Private Instance Methods
agent_base_dir(agent_id)
click to toggle source
# File lib/cloud/dummy.rb, line 379 def agent_base_dir(agent_id) "#{@base_dir}/agent-base-dir-#{agent_id}" end
agent_cmd(agent_id)
click to toggle source
# File lib/cloud/dummy.rb, line 395 def agent_cmd(agent_id) agent_config_file = File.join(agent_base_dir(agent_id), 'agent.json') agent_config = { 'Infrastructure' => { 'Settings' => { 'Sources' => [{ 'Type' => 'File', 'SettingsPath' => agent_settings_file(agent_id) }], 'UseRegistry' => true } } } File.write(agent_config_file, JSON.generate(agent_config)) go_agent_exe = File.expand_path('../../../../go/src/github.com/cloudfoundry/bosh-agent/out/bosh-agent', __FILE__) %W[#{go_agent_exe} -b #{agent_base_dir(agent_id)} -P dummy -M dummy-nats -C #{agent_config_file}] end
agent_id_for_vm_id(vm_cid)
click to toggle source
# File lib/cloud/dummy.rb, line 368 def agent_id_for_vm_id(vm_cid) @vm_repo.load(vm_cid).agent_id end
agent_settings_file(agent_id)
click to toggle source
# File lib/cloud/dummy.rb, line 372 def agent_settings_file(agent_id) # Even though dummy CPI has complete access to agent execution file system # it should never write directly to settings.json because # the agent is responsible for retrieving the settings from the CPI. File.join(agent_base_dir(agent_id), 'bosh', 'dummy-cpi-agent-env.json') end
allocate_ips(ips)
click to toggle source
# File lib/cloud/dummy.rb, line 346 def allocate_ips(ips) ips.each do |ip| begin network_dir = File.join(@base_dir, 'dummy_cpi_networks') FileUtils.makedirs(network_dir) open(File.join(network_dir, ip['ip']), File::WRONLY|File::CREAT|File::EXCL).close rescue Errno::EEXIST # at this point we should actually free all the IPs we successfully allocated before the collision, # but in practice the tests only feed in one IP per VM so that cleanup code would never be exercised raise "IP Address #{ip['ip']} in network '#{ip['network']}' is already in use" end end end
attachment_file(vm_cid, disk_id)
click to toggle source
# File lib/cloud/dummy.rb, line 441 def attachment_file(vm_cid, disk_id) File.join(attachment_path(disk_id), vm_cid) end
attachment_path(disk_id)
click to toggle source
# File lib/cloud/dummy.rb, line 445 def attachment_path(disk_id) File.join(@base_dir, 'attachments', disk_id) end
detach_disks_attached_to_vm(vm_cid)
click to toggle source
# File lib/cloud/dummy.rb, line 433 def detach_disks_attached_to_vm(vm_cid) @logger.info("Detaching disks for vm #{vm_cid}") Dir.glob(attachment_file(vm_cid, '*')) do |file_path| @logger.info("Detaching found attachment #{file_path}") FileUtils.rm_rf(File.dirname(file_path)) end end
disk_attached?(disk_id)
click to toggle source
# File lib/cloud/dummy.rb, line 429 def disk_attached?(disk_id) File.exist?(attachment_path(disk_id)) end
disk_file(disk_id)
click to toggle source
# File lib/cloud/dummy.rb, line 425 def disk_file(disk_id) File.join(@base_dir, 'disks', disk_id) end
free_ips(vm_cid)
click to toggle source
# File lib/cloud/dummy.rb, line 360 def free_ips(vm_cid) return unless @vm_repo.exists?(vm_cid) vm = @vm_repo.load(vm_cid) vm.ips.each do |ip| FileUtils.rm_rf(File.join(@base_dir, 'dummy_cpi_networks', ip['ip'])) end end
parameter_names_to_values(the_method, *the_method_args)
click to toggle source
# File lib/cloud/dummy.rb, line 467 def parameter_names_to_values(the_method, *the_method_args) hash = {} method(the_method).parameters.each_with_index do |param, index| hash[param[1]] = the_method_args[index] end hash end
read_agent_settings(agent_id)
click to toggle source
# File lib/cloud/dummy.rb, line 417 def read_agent_settings(agent_id) JSON.parse(File.read(agent_settings_file(agent_id))) end
record_inputs(method, args)
click to toggle source
# File lib/cloud/dummy.rb, line 463 def record_inputs(method, args) @inputs_recorder.record(method, args) end
snapshot_file(snapshot_id)
click to toggle source
# File lib/cloud/dummy.rb, line 449 def snapshot_file(snapshot_id) File.join(@base_dir, 'snapshots', snapshot_id) end
spawn_agent_process(agent_id)
click to toggle source
# File lib/cloud/dummy.rb, line 324 def spawn_agent_process(agent_id) root_dir = File.join(agent_base_dir(agent_id), 'root_dir') FileUtils.mkdir_p(File.join(root_dir, 'etc', 'logrotate.d')) agent_cmd = agent_cmd(agent_id) agent_log = agent_log_path(agent_id) agent_pid = Process.spawn( { 'TMPDIR' => @tmp_dir }, *agent_cmd, { chdir: agent_base_dir(agent_id), out: agent_log, err: agent_log, } ) Process.detach(agent_pid) agent_pid end
stemcell_file(stemcell_id)
click to toggle source
# File lib/cloud/dummy.rb, line 421 def stemcell_file(stemcell_id) File.join(@base_dir, "stemcell_#{stemcell_id}") end
validate_and_record_inputs(schema, the_method, *args)
click to toggle source
# File lib/cloud/dummy.rb, line 453 def validate_and_record_inputs(schema, the_method, *args) parameter_names_to_values = parameter_names_to_values(the_method, *args) begin schema.validate(parameter_names_to_values) rescue Membrane::SchemaValidationError => err raise ArgumentError, "Invalid arguments sent to #{the_method}: #{err.message}" end record_inputs(the_method, parameter_names_to_values) end
write_agent_default_network(agent_id, ip_address)
click to toggle source
# File lib/cloud/dummy.rb, line 388 def write_agent_default_network(agent_id, ip_address) # Agent looks for following file to resolve default network on dummy infrastructure path = File.join(agent_base_dir(agent_id), 'bosh', 'dummy-default-network-settings.json') FileUtils.mkdir_p(File.dirname(path)) File.write(path, JSON.generate('ip' => ip_address)) end
write_agent_settings(agent_id, settings)
click to toggle source
# File lib/cloud/dummy.rb, line 383 def write_agent_settings(agent_id, settings) FileUtils.mkdir_p(File.dirname(agent_settings_file(agent_id))) File.write(agent_settings_file(agent_id), JSON.generate(settings)) end