Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions src/bosh-director/lib/bosh/director.rb
Original file line number Diff line number Diff line change
Expand Up @@ -237,6 +237,8 @@ module Director
require 'bosh/director/jobs/helpers'
require 'bosh/director/jobs/db_job'
require 'bosh/director/jobs/orphan_disk'
require 'bosh/director/jobs/dynamic_disks/create_dynamic_disk'
require 'bosh/director/jobs/dynamic_disks/attach_dynamic_disk'
require 'bosh/director/jobs/dynamic_disks/provide_dynamic_disk'
require 'bosh/director/jobs/dynamic_disks/detach_dynamic_disk'
require 'bosh/director/jobs/dynamic_disks/delete_dynamic_disk'
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,60 @@ module Api::Controllers
class DynamicDisksController < BaseController
include ValidationHelper

get '/', scope: :list_dynamic_disks do
disks = Models::DynamicDisk.eager(:deployment, vm: :instance).all.map do |disk|
{
name: disk.name,
disk_cid: disk.disk_cid,
deployment: disk.deployment&.name,
instance: disk.vm&.instance&.name,
availability_zone: disk.availability_zone,
size: disk.size,
disk_pool_name: disk.disk_pool_name,
cpi: disk.cpi,
metadata: disk.metadata,
}
end
json_encode(disks)
end
Comment thread
coderabbitai[bot] marked this conversation as resolved.

post '/', scope: :create_dynamic_disks, consumes: :json do
request_hash = JSON.parse(request.body.read)

deployment_name = safe_property(request_hash, 'deployment_name', class: String, min_length: 1)
az = safe_property(request_hash, 'az', class: String, min_length: 1)
disk_name = safe_property(request_hash, 'disk_name', class: String, min_length: 1)
disk_pool_name = safe_property(request_hash, 'disk_pool_name', class: String, min_length: 1)
disk_size = safe_property(request_hash, 'disk_size', class: Integer, min: 1)
metadata = safe_property(request_hash, 'metadata', class: Hash, optional: true)

task = JobQueue.new.enqueue(
current_user,
Jobs::DynamicDisks::CreateDynamicDisk,
'create dynamic disk',
[deployment_name, az, disk_name, disk_pool_name, disk_size, metadata],
)

redirect "/tasks/#{task.id}"
end

post '/:disk_name/attach', scope: :attach_dynamic_disks, consumes: :json do
disk_name = safe_property(params, 'disk_name', class: String, min_length: 1)

request_hash = JSON.parse(request.body.read)
instance_id = safe_property(request_hash, 'instance_id', class: String, min_length: 1)
metadata = safe_property(request_hash, 'metadata', class: Hash, optional: true)

task = JobQueue.new.enqueue(
current_user,
Jobs::DynamicDisks::AttachDynamicDisk,
'attach dynamic disk',
[disk_name, instance_id, metadata],
)

redirect "/tasks/#{task.id}"
end

post '/provide', scope: :provide_dynamic_disks, consumes: :json do
request_hash = JSON.parse(request.body.read)

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
module Bosh::Director
module Jobs::DynamicDisks
class AttachDynamicDisk < Jobs::BaseJob
include Jobs::Helpers::DynamicDiskHelpers
include LockHelper

@queue = :dynamic_disks

def self.job_type
:attach_dynamic_disk
end

def initialize(disk_name, instance_id, metadata = nil)
@disk_name = disk_name
@instance_id = instance_id
@metadata = metadata
end

def perform
disk_model = Models::DynamicDisk.find(name: @disk_name)
raise "disk `#{@disk_name}` not found" if disk_model.nil?

instance = Models::Instance.find(uuid: @instance_id)
raise "instance `#{@instance_id}` not found" if instance.nil?

vm = instance.active_vm
raise "no active vm found for instance `#{@instance_id}`" if vm.nil?

already_attached_same_vm = false
unless disk_model.vm.nil?
if disk_model.vm.id == vm.id
already_attached_same_vm = true
else
raise "disk `#{@disk_name}` is already attached to a different vm `#{disk_model.vm.cid}`"
end
end

disk_az = disk_model.availability_zone
vm_az = vm.instance.availability_zone
unless disk_az.nil? || vm_az.nil? || disk_az == vm_az
raise "disk `#{@disk_name}` is in AZ `#{disk_az}` but instance `#{@instance_id}` is in AZ `#{vm_az}`"
end

cloud = Bosh::Director::CloudFactory.create.get(disk_model.cpi, vm.stemcell_api_version)

unless already_attached_same_vm
disk_hint = with_vm_lock(vm.cid, timeout: VM_LOCK_TIMEOUT) { cloud.attach_disk(vm.cid, disk_model.disk_cid) }
disk_model.update(vm_id: vm.id, availability_zone: vm.instance.availability_zone, disk_hint: disk_hint)
end

if !@metadata.nil? && disk_model.metadata != @metadata
MetadataUpdater.build.update_dynamic_disk_metadata(cloud, disk_model, @metadata)
disk_model.update(metadata: @metadata)
end

agent_client = AgentClient.with_agent_id(vm.agent_id, instance.name)
agent_client.add_dynamic_disk(disk_model.disk_cid, disk_model.disk_hint)

"attached disk `#{@disk_name}` to vm `#{vm.cid}` in deployment `#{instance.deployment.name}`"
end
end
end
end
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
module Bosh::Director
module Jobs::DynamicDisks
class CreateDynamicDisk < Jobs::BaseJob
include Jobs::Helpers::DynamicDiskHelpers

@queue = :dynamic_disks

def self.job_type
:create_dynamic_disk
end

def initialize(deployment_name, az, disk_name, disk_pool_name, disk_size, metadata)
@deployment_name = deployment_name
@az = az
@disk_name = disk_name
@disk_pool_name = disk_pool_name
@disk_size = disk_size
@metadata = metadata
end

def perform
disk_model = Models::DynamicDisk.find(name: @disk_name)
raise "disk `#{@disk_name}` already exists" unless disk_model.nil?

deployment = Models::Deployment.find(name: @deployment_name)
raise "deployment `#{@deployment_name}` not found" if deployment.nil?

# Find an active VM in the requested AZ to use as the hint for create_disk.
# The IaaS uses the VM's location to determine which AZ/datastore to place the disk in.
vm = find_active_vm_in_az(deployment, @az)
raise "no active VM found in deployment `#{@deployment_name}` in AZ `#{@az}`" if vm.nil?

cloud = Bosh::Director::CloudFactory.create.get(vm.cpi)

cloud_properties = find_disk_cloud_properties(deployment, @disk_pool_name).clone
cloud_properties['name'] = @disk_name

disk_cid = cloud.create_disk(@disk_size, cloud_properties, vm.cid)
begin
disk_model = Models::DynamicDisk.create(
name: @disk_name,
disk_cid: disk_cid,
deployment_id: deployment.id,
size: @disk_size,
disk_pool_name: @disk_pool_name,
cpi: vm.cpi,
availability_zone: @az,
)
rescue
cloud.delete_disk(disk_cid)
raise
end

if !@metadata.nil? && disk_model.metadata != @metadata
MetadataUpdater.build.update_dynamic_disk_metadata(cloud, disk_model, @metadata)
disk_model.update(metadata: @metadata)
end

disk_info = { disk_cid: disk_model.disk_cid }

task_result.write(JSON.generate(disk_info))
task_result.write("\n")

"created disk `#{@disk_name}` in deployment `#{deployment.name}` in AZ `#{@az}`"
end

private

def find_active_vm_in_az(deployment, az)
deployment.instances
.select { |i| i.availability_zone == az }
.flat_map { |i| i.vms.select(&:active) }
.first
end
end
end
end
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,9 @@ module Jobs::Helpers
module DynamicDiskHelpers
VM_LOCK_TIMEOUT = 60

def find_disk_cloud_properties(instance, disk_pool_name)
teams = instance.deployment.teams
def find_disk_cloud_properties(deployment_or_instance, disk_pool_name)
deployment = deployment_or_instance.is_a?(Models::Deployment) ? deployment_or_instance : deployment_or_instance.deployment
teams = deployment.teams
configs = Models::Config.latest_set_for_teams('cloud', *teams)
raise 'No cloud configs provided' if configs.empty?

Expand All @@ -16,4 +17,4 @@ def find_disk_cloud_properties(instance, disk_pool_name)
end
end
end
end
end
1 change: 1 addition & 0 deletions src/bosh-director/spec/support/test_identity_provider.rb
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ def user_scopes
'dynamic-disks-attacher' => ['bosh.dynamic-disks.attach'],
'dynamic-disks-detacher' => ['bosh.dynamic-disks.detach'],
'dynamic-disks-deleter' => ['bosh.dynamic-disks.delete'],
'dynamic-disks-lister' => ['bosh.dynamic-disks.list'],
'outsider' => ['uaa.admin'],
}
end
Expand Down
Loading
Loading