func DeleteDisk(c config.Cpi, extInput bosh.MethodArguments) error {
	var diskCID string

	if reflect.TypeOf(extInput[0]) != reflect.TypeOf(diskCID) {
		return errors.New("Received unexpected type for disk cid")
	}

	diskCID = extInput[0].(string)

	nodes, err := rackhdapi.GetNodes(c)
	if err != nil {
		return err
	}

	for _, node := range nodes {
		if node.PersistentDisk.DiskCID == diskCID {
			if node.PersistentDisk.IsAttached {
				return fmt.Errorf("Disk: %s is attached\n", diskCID)
			}

			container := rackhdapi.PersistentDiskSettingsContainer{
				PersistentDisk: rackhdapi.PersistentDiskSettings{},
			}
			bodyBytes, err := json.Marshal(container)
			if err != nil {
				return err
			}

			rackhdapi.PatchNode(c, node.ID, bodyBytes)
			return nil
		}
	}

	return fmt.Errorf("Disk: %s not found\n", diskCID)
}
func CreateDisk(c config.Cpi, extInput bosh.MethodArguments) (string, error) {
	diskSizeInMB, vmCID, err := parseCreateDiskInput(extInput)
	if err != nil {
		return "", err
	}

	filter := Filter{
		data:   diskSizeInMB,
		method: FilterBasedOnSizeMethod,
	}
	var diskCID string
	var node rackhdapi.Node
	if vmCID != "" {
		node, err = rackhdapi.GetNodeByVMCID(c, vmCID)
		if err != nil {
			return "", err
		}

		if node.PersistentDisk.DiskCID != "" {
			return "", fmt.Errorf("error creating disk: VM %s already has a persistent disk", vmCID)
		}

		valid, err := filter.Run(c, node)
		if !valid || err != nil {
			return "", fmt.Errorf("error creating disk: %v", err)
		}

		if node.PersistentDisk.PregeneratedDiskCID == "" {
			return "", fmt.Errorf("error creating disk: can not find pregenerated disk cid for VM %s", vmCID)
		}
		diskCID = node.PersistentDisk.PregeneratedDiskCID

	} else {
		node.ID, err = TryReservationWithFilter(c, "", filter, SelectNodeFromRackHD, ReserveNodeFromRackHD)
		if err != nil {
			return "", err
		}
		diskCID = fmt.Sprintf("%s-%s", node.ID, c.RequestID)
	}

	container := rackhdapi.PersistentDiskSettingsContainer{
		PersistentDisk: rackhdapi.PersistentDiskSettings{
			DiskCID:    diskCID,
			Location:   fmt.Sprintf("/dev/%s", rackhdapi.PersistentDiskLocation),
			IsAttached: false,
		},
	}

	bodyBytes, err := json.Marshal(container)
	if err != nil {
		return "", fmt.Errorf("error marshalling persistent disk information for VM %s", vmCID)
	}

	err = rackhdapi.PatchNode(c, node.ID, bodyBytes)
	if err != nil {
		return "", err
	}

	return container.PersistentDisk.DiskCID, nil
}
func CreateDisk(c config.Cpi, extInput bosh.MethodArguments) (string, error) {
	diskSizeInMB, vmCID, err := parseCreateDiskInput(extInput)
	if err != nil {
		return "", err
	}

	var node rackhdapi.Node
	if vmCID != "" {
		node, err = rackhdapi.GetNodeByVMCID(c, vmCID)
		if err != nil {
			return "", err
		}

		if node.PersistentDisk.DiskCID != "" {
			return "", fmt.Errorf("error creating disk: VM %s already has a persistent disk", vmCID)
		}
	} else {
		nodeID, err := SelectNodeFromRackHD(c, "")
		if err != nil {
			return "", err
		}
		node.ID = nodeID
	}

	catalog, err := rackhdapi.GetNodeCatalog(c, node.ID)
	if err != nil {
		return "", fmt.Errorf("error getting catalog of VM: %s", vmCID)
	}

	availableSpaceInKB, err := strconv.Atoi(catalog.Data.BlockDevices[rackhdapi.PersistentDiskLocation].Size)
	if err != nil {
		return "", fmt.Errorf("error creating disk for VM %s: disk not found", vmCID)
	}

	if availableSpaceInKB < diskSizeInMB*1024 {
		return "", fmt.Errorf("error creating disk with size %vMB for VM %s: insufficient available disk space", diskSizeInMB, vmCID)
	}

	container := rackhdapi.PersistentDiskSettingsContainer{
		PersistentDisk: rackhdapi.PersistentDiskSettings{
			DiskCID:    node.ID,
			Location:   fmt.Sprintf("/dev/%s", rackhdapi.PersistentDiskLocation),
			IsAttached: false,
		},
	}

	bodyBytes, err := json.Marshal(container)
	if err != nil {
		return "", fmt.Errorf("error marshalling persistent disk information for VM %s", vmCID)
	}

	err = rackhdapi.PatchNode(c, node.ID, bodyBytes)
	if err != nil {
		return "", err
	}

	return node.ID, nil
}
func AttachDisk(c config.Cpi, extInput bosh.MethodArguments) error {
	var vmCID string
	var diskCID string

	if reflect.TypeOf(extInput[0]) != reflect.TypeOf(vmCID) {
		return errors.New("Received unexpected type for vm cid")
	}

	if reflect.TypeOf(extInput[1]) != reflect.TypeOf(diskCID) {
		return errors.New("Received unexpected type for disk cid")
	}

	vmCID = extInput[0].(string)
	diskCID = extInput[1].(string)

	node, err := rackhdapi.GetNodeByVMCID(c, vmCID)
	if err != nil {
		return fmt.Errorf("VM: %s not found\n", vmCID)
	}

	if node.PersistentDisk.DiskCID == "" {
		return fmt.Errorf("Disk: %s not found on VM: %s", diskCID, vmCID)
	}

	if node.PersistentDisk.DiskCID != diskCID {
		if node.PersistentDisk.IsAttached {
			return fmt.Errorf("Node %s has persistent disk %s attached. Cannot attach additional disk %s.", vmCID, node.PersistentDisk.DiskCID, diskCID)
		} else {
			return fmt.Errorf("Node %s has persistent disk %s, but detached. Cannot attach disk %s.", vmCID, node.PersistentDisk.DiskCID, diskCID)
		}
	}

	if node.CID != vmCID {
		return fmt.Errorf("Disk: %s does not belong to VM: %s\n", diskCID, vmCID)
	}

	if !node.PersistentDisk.IsAttached {
		container := rackhdapi.PersistentDiskSettingsContainer{
			PersistentDisk: node.PersistentDisk,
		}
		container.PersistentDisk.IsAttached = true

		bodyBytes, err := json.Marshal(container)
		if err != nil {
			return err
		}

		err = rackhdapi.PatchNode(c, node.ID, bodyBytes)
		if err != nil {
			return err
		}
	}

	return nil
}
func DetachDisk(c config.Cpi, extInput bosh.MethodArguments) error {
	var vmCID string
	var diskCID string

	if reflect.TypeOf(extInput[0]) != reflect.TypeOf(vmCID) {
		return errors.New("Received unexpected type for vm cid")
	}

	if reflect.TypeOf(extInput[1]) != reflect.TypeOf(diskCID) {
		return errors.New("Received unexpected type for disk cid")
	}

	vmCID = extInput[0].(string)
	diskCID = extInput[1].(string)

	nodes, err := rackhdapi.GetNodes(c)
	if err != nil {
		return err
	}

	for _, node := range nodes {
		if node.PersistentDisk.DiskCID == diskCID {
			if !node.PersistentDisk.IsAttached {
				return fmt.Errorf("Disk: %s is detached\n", diskCID)
			}

			if node.CID != vmCID {
				return fmt.Errorf("Disk %s does not belong to VM %s\n", diskCID, vmCID)
			}

			container := rackhdapi.PersistentDiskSettingsContainer{
				PersistentDisk: node.PersistentDisk,
			}
			container.PersistentDisk.IsAttached = false

			bodyBytes, err := json.Marshal(container)
			if err != nil {
				return err
			}

			rackhdapi.PatchNode(c, node.ID, bodyBytes)
			return nil
		}
	}

	return fmt.Errorf("Disk: %s not found\n", diskCID)
}
func CreateVM(c config.Cpi, extInput bosh.MethodArguments) (string, error) {
	agentID, stemcellCID, publicKey, boshNetworks, nodeID, err := parseCreateVMInput(extInput)
	if err != nil {
		return "", err
	}

	nodeID, err = TryReservation(c, nodeID, SelectNodeFromRackHD, ReserveNodeFromRackHD)
	if err != nil {
		return "", err
	}

	var netSpec bosh.Network
	var netName string
	for k, v := range boshNetworks {
		netName = k
		netSpec = v
	}

	nodeCatalog, err := rackhdapi.GetNodeCatalog(c, nodeID)
	if err != nil {
		return "", err
	}

	if netSpec.NetworkType == bosh.ManualNetworkType {
		netSpec, err = attachMAC(nodeCatalog.Data.NetworkData.Networks, netSpec)
		if err != nil {
			return "", err
		}
	}

	node, err := rackhdapi.GetNode(c, nodeID)
	if err != nil {
		return "", err
	}

	var diskCID string
	if node.PersistentDisk.DiskCID == "" {
		diskCID = fmt.Sprintf("%s-%s", nodeID, c.RequestID)

		container := rackhdapi.PersistentDiskSettingsContainer{
			PersistentDisk: rackhdapi.PersistentDiskSettings{
				PregeneratedDiskCID: diskCID,
			},
		}

		bodyBytes, err := json.Marshal(container)
		if err != nil {
			return "", fmt.Errorf("error marshalling persistent disk information for agent %s", agentID)
		}

		err = rackhdapi.PatchNode(c, node.ID, bodyBytes)
		if err != nil {
			return "", err
		}
	} else {
		diskCID = node.PersistentDisk.DiskCID
	}

	persistentMetadata := map[string]interface{}{}
	if _, sdbFound := nodeCatalog.Data.BlockDevices["sdb"]; sdbFound {
		persistentMetadata = map[string]interface{}{
			diskCID: map[string]string{
				"path": "/dev/sdb",
			},
		}
	}

	env := bosh.AgentEnv{
		AgentID:   agentID,
		Blobstore: c.Agent.Blobstore,
		Disks: map[string]interface{}{
			"system":     "/dev/sda",
			"persistent": persistentMetadata,
		},
		Mbus:     c.Agent.Mbus,
		Networks: map[string]bosh.Network{netName: netSpec},
		NTP:      c.Agent.Ntp,
		VM: map[string]string{
			"id":   nodeID,
			"name": nodeID,
		},
		PublicKey: publicKey,
	}

	envBytes, err := json.Marshal(env)
	if err != nil {
		return "", fmt.Errorf("error marshalling agent env %s", err)
	}
	envReader := bytes.NewReader(envBytes)
	vmCID, err := rackhdapi.UploadFile(c, nodeID, envReader, int64(len(envBytes)))
	if err != nil {
		return "", err
	}
	defer rackhdapi.DeleteFile(c, nodeID)

	workflowName, err := workflows.PublishProvisionNodeWorkflow(c)
	if err != nil {
		return "", fmt.Errorf("error publishing provision workflow: %s", err)
	}

	wipeDisk := (nodeID == "")

	err = workflows.RunProvisionNodeWorkflow(c, nodeID, workflowName, vmCID, stemcellCID, wipeDisk)
	if err != nil {
		return "", fmt.Errorf("error running provision workflow: %s", err)
	}

	return vmCID, nil
}