lxd/container_lxc.go
func (c *containerLXC) getDiskLimits() (map[string]deviceBlockLimit, error) {
	result := map[string]deviceBlockLimit{}

	// Build a list of all valid block devices
	validBlocks := []string{}

	dents, err := ioutil.ReadDir("/sys/class/block/")
	if err != nil {
		return nil, err
	}

	for _, f := range dents {
		fPath := filepath.Join("/sys/class/block/", f.Name())
		if shared.PathExists(fmt.Sprintf("%s/partition", fPath)) {
			continue
		}

		if !shared.PathExists(fmt.Sprintf("%s/dev", fPath)) {
			continue
		}

		block, err := ioutil.ReadFile(fmt.Sprintf("%s/dev", fPath))
		if err != nil {
			return nil, err
		}

		validBlocks = append(validBlocks, strings.TrimSuffix(string(block), "\n"))
	}

	// Process all the limits
	blockLimits := map[string][]deviceBlockLimit{}
	for _, k := range c.expandedDevices.DeviceNames() {
		m := c.expandedDevices[k]
		if m["type"] != "disk" {
			continue
		}

		// Apply max limit
		if m["limits.max"] != "" {
			m["limits.read"] = m["limits.max"]
			m["limits.write"] = m["limits.max"]
		}

		// Parse the user input
		readBps, readIops, writeBps, writeIops, err := deviceParseDiskLimit(m["limits.read"], m["limits.write"])
		if err != nil {
			return nil, err
		}

		// Set the source path
		source := shared.HostPath(m["source"])
		if source == "" {
			source = c.RootfsPath()
		}

		// Don't try to resolve the block device behind a non-existing path
		if !shared.PathExists(source) {
			continue
		}

		// Get the backing block devices (major:minor)
		blocks, err := deviceGetParentBlocks(source)
		if err != nil {
			if readBps == 0 && readIops == 0 && writeBps == 0 && writeIops == 0 {
				// If the device doesn't exist, there is no limit to clear so ignore the failure
				continue
			} else {
				return nil, err
			}
		}

		device := deviceBlockLimit{readBps: readBps, readIops: readIops, writeBps: writeBps, writeIops: writeIops}
		for _, block := range blocks {
			blockStr := ""

			if shared.StringInSlice(block, validBlocks) {
				// Straightforward entry (full block device)
				blockStr = block
			} else {
				// Attempt to deal with a partition (guess its parent)
				fields := strings.SplitN(block, ":", 2)
				fields[1] = "0"
				if shared.StringInSlice(fmt.Sprintf("%s:%s", fields[0], fields[1]), validBlocks) {
					blockStr = fmt.Sprintf("%s:%s", fields[0], fields[1])
				}
			}

			if blockStr == "" {
				return nil, fmt.Errorf("Block device doesn't support quotas: %s", block)
			}

			if blockLimits[blockStr] == nil {
				blockLimits[blockStr] = []deviceBlockLimit{}
			}
			blockLimits[blockStr] = append(blockLimits[blockStr], device)
		}
	}

	// Average duplicate limits
	for block, limits := range blockLimits {
		var readBpsCount, readBpsTotal, readIopsCount, readIopsTotal, writeBpsCount, writeBpsTotal, writeIopsCount, writeIopsTotal int64

		for _, limit := range limits {
			if limit.readBps > 0 {
				readBpsCount += 1
				readBpsTotal += limit.readBps
			}

			if limit.readIops > 0 {
				readIopsCount += 1
				readIopsTotal += limit.readIops
			}

			if limit.writeBps > 0 {
				writeBpsCount += 1
				writeBpsTotal += limit.writeBps
			}

			if limit.writeIops > 0 {
				writeIopsCount += 1
				writeIopsTotal += limit.writeIops
			}
		}

		device := deviceBlockLimit{}

		if readBpsCount > 0 {
			device.readBps = readBpsTotal / readBpsCount
		}

		if readIopsCount > 0 {
			device.readIops = readIopsTotal / readIopsCount
		}

		if writeBpsCount > 0 {
			device.writeBps = writeBpsTotal / writeBpsCount
		}

		if writeIopsCount > 0 {
			device.writeIops = writeIopsTotal / writeIopsCount
		}

		result[block] = device
	}

	return result, nil
}
func (c *containerLXC) getDiskLimits() (map[string]deviceBlockLimit, error) {
	result := map[string]deviceBlockLimit{}

	// Build a list of all valid block devices
	validBlocks := []string{}

	dents, err := ioutil.ReadDir("/sys/class/block/")
	if err != nil {
		return nil, err
	}

	for _, f := range dents {
		fPath := filepath.Join("/sys/class/block/", f.Name())
		if shared.PathExists(fmt.Sprintf("%s/partition", fPath)) {
			continue
		}

		if !shared.PathExists(fmt.Sprintf("%s/dev", fPath)) {
			continue
		}

		block, err := ioutil.ReadFile(fmt.Sprintf("%s/dev", fPath))
		if err != nil {
			return nil, err
		}

		validBlocks = append(validBlocks, strings.TrimSuffix(string(block), "\n"))
	}

	// Process all the limits
	blockLimits := map[string][]deviceBlockLimit{}
	for _, k := range c.expandedDevices.DeviceNames() {
		m := c.expandedDevices[k]
		if m["type"] != "disk" {
			continue
		}

		// Apply max limit
		if m["limits.max"] != "" {
			m["limits.read"] = m["limits.max"]
			m["limits.write"] = m["limits.max"]
		}

		// Parse the user input
		readBps, readIops, writeBps, writeIops, err := deviceParseDiskLimit(m["limits.read"], m["limits.write"])
		if err != nil {
			return nil, err
		}

		// Set the source path
		source := shared.HostPath(m["source"])
		if source == "" {
			source = c.RootfsPath()
		}

		// Don't try to resolve the block device behind a non-existing path
		if !shared.PathExists(source) {
			continue
		}

		// Get the backing block devices (major:minor)
		blocks, err := deviceGetParentBlocks(source)
		if err != nil {
			if readBps == 0 && readIops == 0 && writeBps == 0 && writeIops == 0 {
				// If the device doesn't exist, there is no limit to clear so ignore the failure
				continue
			} else {
				return nil, err
			}
		}

		device := deviceBlockLimit{readBps: readBps, readIops: readIops, writeBps: writeBps, writeIops: writeIops}
		for _, block := range blocks {
			blockStr := ""

			if shared.StringInSlice(block, validBlocks) {
				// Straightforward entry (full block device)
				blockStr = block
			} else {
				// Attempt to deal with a partition (guess its parent)
				fields := strings.SplitN(block, ":", 2)
				fields[1] = "0"
				if shared.StringInSlice(fmt.Sprintf("%s:%s", fields[0], fields[1]), validBlocks) {
					blockStr = fmt.Sprintf("%s:%s", fields[0], fields[1])
				}
			}

			if blockStr == "" {
				return nil, fmt.Errorf("Block device doesn't support quotas: %s", block)
			}

			if blockLimits[blockStr] == nil {
				blockLimits[blockStr] = []deviceBlockLimit{}
			}
			blockLimits[blockStr] = append(blockLimits[blockStr], device)
		}
	}

	// Average duplicate limits
	for block, limits := range blockLimits {
		var readBpsCount, readBpsTotal, readIopsCount, readIopsTotal, writeBpsCount, writeBpsTotal, writeIopsCount, writeIopsTotal int64

		for _, limit := range limits {
			if limit.readBps > 0 {
				readBpsCount += 1
				readBpsTotal += limit.readBps
			}

			if limit.readIops > 0 {
				readIopsCount += 1
				readIopsTotal += limit.readIops
			}

			if limit.writeBps > 0 {
				writeBpsCount += 1
				writeBpsTotal += limit.writeBps
			}

			if limit.writeIops > 0 {
				writeIopsCount += 1
				writeIopsTotal += limit.writeIops
			}
		}

		device := deviceBlockLimit{}

		if readBpsCount > 0 {
			device.readBps = readBpsTotal / readBpsCount
		}

		if readIopsCount > 0 {
			device.readIops = readIopsTotal / readIopsCount
		}

		if writeBpsCount > 0 {
			device.writeBps = writeBpsTotal / writeBpsCount
		}

		if writeIopsCount > 0 {
			device.writeIops = writeIopsTotal / writeIopsCount
		}

		result[block] = device
	}

	return result, nil
}
func ( c * containerLXC ) getDiskLimits ( ) ( map [ string ] deviceBlockLimit , error ) { result := map [ string ] deviceBlockLimit { } // Build a list of all valid block devices validBlocks := [ ] string { } dents , err := ioutil . ReadDir ( " " ) if err != nil { return nil , err } for _ , f := range dents { fPath := filepath . Join ( " " , f . Name ( ) ) if shared . PathExists ( fmt . Sprintf ( " " , fPath ) ) { continue } if ! shared . PathExists ( fmt . Sprintf ( " " , fPath ) ) { continue } block , err := ioutil . ReadFile ( fmt . Sprintf ( " " , fPath ) ) if err != nil { return nil , err } validBlocks = append ( validBlocks , strings . TrimSuffix ( string ( block ) , " \n " ) ) } // Process all the limits blockLimits := map [ string ] [ ] deviceBlockLimit { } for _ , k := range c . expandedDevices . DeviceNames ( ) { m := c . expandedDevices [ k ] if m [ " " ] != " " { continue } // Apply max limit if m [ " " ] != " " { m [ " " ] = m [ " " ] m [ " " ] = m [ " " ] } // Parse the user input readBps , readIops , writeBps , writeIops , err := deviceParseDiskLimit ( m [ " " ] , m [ " " ] ) if err != nil { return nil , err } // Set the source path source := shared . HostPath ( m [ " " ] ) if source == " " { source = c . RootfsPath ( ) } // Don't try to resolve the block device behind a non-existing path if ! shared . PathExists ( source ) { continue } // Get the backing block devices (major:minor) blocks , err := deviceGetParentBlocks ( source ) if err != nil { if readBps == 0 && readIops == 0 && writeBps == 0 && writeIops == 0 { // If the device doesn't exist, there is no limit to clear so ignore the failure continue } else { return nil , err } } device := deviceBlockLimit { readBps : readBps , readIops : readIops , writeBps : writeBps , writeIops : writeIops } for _ , block := range blocks { blockStr := " " if shared . StringInSlice ( block , validBlocks ) { // Straightforward entry (full block device) blockStr = block } else { // Attempt to deal with a partition (guess its parent) fields := strings . SplitN ( block , " " , 2 ) fields [ 1 ] = " " if shared . StringInSlice ( fmt . Sprintf ( " " , fields [ 0 ] , fields [ 1 ] ) , validBlocks ) { blockStr = fmt . Sprintf ( " " , fields [ 0 ] , fields [ 1 ] ) } } if blockStr == " " { return nil , fmt . Errorf ( " " , block ) } if blockLimits [ blockStr ] == nil { blockLimits [ blockStr ] = [ ] deviceBlockLimit { } } blockLimits [ blockStr ] = append ( blockLimits [ blockStr ] , device ) } } // Average duplicate limits for block , limits := range blockLimits { var readBpsCount , readBpsTotal , readIopsCount , readIopsTotal , writeBpsCount , writeBpsTotal , writeIopsCount , writeIopsTotal int64 for _ , limit := range limits { if limit . readBps > 0 { readBpsCount += 1 readBpsTotal += limit . readBps } if limit . readIops > 0 { readIopsCount += 1 readIopsTotal += limit . readIops } if limit . writeBps > 0 { writeBpsCount += 1 writeBpsTotal += limit . writeBps } if limit . writeIops > 0 { writeIopsCount += 1 writeIopsTotal += limit . writeIops } } device := deviceBlockLimit { } if readBpsCount > 0 { device . readBps = readBpsTotal / readBpsCount } if readIopsCount > 0 { device . readIops = readIopsTotal / readIopsCount } if writeBpsCount > 0 { device . writeBps = writeBpsTotal / writeBpsCount } if writeIopsCount > 0 { device . writeIops = writeIopsTotal / writeIopsCount } result [ block ] = device } return result , nil }
// Block I/O limits
Block I / O limits