<commit_before>package db

import (
	"fmt"
	"go/ast"
	"net/url"
	"path/filepath"
	"reflect"
	"runtime"
	"sort"
	"strings"

	"github.com/lxc/lxd/lxd/db/generate/lex"
	"github.com/lxc/lxd/shared"
	"github.com/pkg/errors"
)

// Packages returns the the AST packages in which to search for structs.
//
// By default it includes the lxd/db and shared/api packages.
func Packages() (map[string]*ast.Package, error) {
	packages := map[string]*ast.Package{}

	_, filename, _, _ := runtime.Caller(0)

	for _, name := range defaultPackages {
		pkg, err := lex.Parse(filepath.Join(filepath.Dir(filename), "..", "..", "..", "..", name))
		if err != nil {
			return nil, errors.Wrapf(err, "Parse %q", name)
		}
		parts := strings.Split(name, "/")
		packages[parts[len(parts)-1]] = pkg
	}

	return packages, nil
}

var defaultPackages = []string{
	"shared/api",
	"lxd/db",
}

// FiltersFromStmt parses all filtering statement defined for the given entity. It
// returns all supported combinations of filters, sorted by number of criteria, and
// the corresponding set of unused filters from the Filter struct.
func FiltersFromStmt(pkg *ast.Package, kind string, entity string, filters []*Field) ([][]string, [][]string) {
	objects := pkg.Scope.Objects
	stmtFilters := [][]string{}

	prefix := fmt.Sprintf("%s%sBy", lex.Minuscule(lex.Camel(entity)), lex.Camel(kind))

	for name := range objects {
		if !strings.HasPrefix(name, prefix) {
			continue
		}
		rest := name[len(prefix):]
		stmtFilters = append(stmtFilters, strings.Split(rest, "And"))
	}

	stmtFilters = sortFilters(stmtFilters)
	ignoredFilters := [][]string{}

	for _, filterGroup := range stmtFilters {
		ignoredFilterGroup := []string{}
		for _, filter := range filters {
			if !shared.StringInSlice(filter.Name, filterGroup) {
				ignoredFilterGroup = append(ignoredFilterGroup, filter.Name)
			}
		}
		ignoredFilters = append(ignoredFilters, ignoredFilterGroup)
	}

	return stmtFilters, ignoredFilters
}

// RefFiltersFromStmt parses all filtering statement defined for the given entity reference.
func RefFiltersFromStmt(pkg *ast.Package, entity string, ref string, filters []*Field) ([][]string, [][]string) {
	objects := pkg.Scope.Objects
	stmtFilters := [][]string{}

	prefix := fmt.Sprintf("%s%sRefBy", lex.Minuscule(lex.Camel(entity)), lex.Capital(ref))

	for name := range objects {
		if !strings.HasPrefix(name, prefix) {
			continue
		}
		rest := name[len(prefix):]
		stmtFilters = append(stmtFilters, strings.Split(rest, "And"))
	}

	stmtFilters = sortFilters(stmtFilters)
	ignoredFilters := [][]string{}

	for _, filterGroup := range stmtFilters {
		ignoredFilterGroup := []string{}
		for _, filter := range filters {
			if !shared.StringInSlice(filter.Name, filterGroup) {
				ignoredFilterGroup = append(ignoredFilterGroup, filter.Name)
			}
		}
		ignoredFilters = append(ignoredFilters, ignoredFilterGroup)
	}

	return stmtFilters, ignoredFilters
}

func sortFilters(filters [][]string) [][]string {
	sort.Slice(filters, func(i, j int) bool {
		n1 := len(filters[i])
		n2 := len(filters[j])
		if n1 != n2 {
			return n1 > n2
		}
		f1 := sortFilter(filters[i])
		f2 := sortFilter(filters[j])
		for k := range f1 {
			if f1[k] == f2[k] {
				continue
			}
			return f1[k] > f2[k]
		}
		panic("duplicate filter")
	})
	return filters
}

func sortFilter(filter []string) []string {
	f := make([]string, len(filter))
	copy(f, filter)
	sort.Sort(sort.Reverse(sort.StringSlice(f)))
	return f
}

// Parse the structure declaration with the given name found in the given Go package.
// Any 'Entity' struct should also have an 'EntityFilter' struct defined in the same file.
func Parse(pkg *ast.Package, name string, kind string) (*Mapping, error) {
	// The main entity struct.
	str := findStruct(pkg.Scope, name)
	if str == nil {
		return nil, fmt.Errorf("No declaration found for %q", name)
	}

	fields, err := parseStruct(str, kind)
	if err != nil {
		return nil, errors.Wrapf(err, "Failed to parse %q", name)
	}

	m := &Mapping{
		Package: pkg.Name,
		Name:    name,
		Fields:  fields,
	}

	// The 'EntityFilter' struct. This is used for filtering on specific fields of the entity.
	filterName := name + "Filter"
	filterStr := findStruct(pkg.Scope, filterName)
	if filterStr == nil {
		return nil, fmt.Errorf("No declaration found for %q", filterName)
	}

	filters, err := parseStruct(filterStr, kind)
	if err != nil {
		return nil, errors.Wrapf(err, "Failed to parse %q", name)
	}

	for i, filter := range filters {
		// Any field in EntityFilter must be present in the original struct.
		field := m.FieldByName(filter.Name)
		if field == nil {
			return nil, fmt.Errorf("Filter field %q is not in struct %q", filter.Name, name)
		}

		// Assign the config tags from the main entity struct to the Filter struct.
		filters[i].Config = field.Config

		// A Filter field and its indirect references must all be in the Filter struct.
		if field.IsIndirect() {
			indirectField := lex.Camel(field.Config.Get("via"))
			for i, f := range filters {
				if f.Name == indirectField {
					break
				}
				if i == len(filters)-1 {
					return nil, fmt.Errorf("Field %q requires field %q in struct %q", field.Name, indirectField, name+"Filter")
				}
			}
		}
	}

	m.Filters = filters

	return m, nil
}

// Find the StructType node for the structure with the given name
func findStruct(scope *ast.Scope, name string) *ast.StructType {
	obj := scope.Lookup(name)
	if obj == nil {
		return nil
	}

	typ, ok := obj.Decl.(*ast.TypeSpec)
	if !ok {
		return nil
	}

	str, ok := typ.Type.(*ast.StructType)
	if !ok {
		return nil
	}

	return str
}

// Extract field information from the given structure.
func parseStruct(str *ast.StructType, kind string) ([]*Field, error) {
	fields := make([]*Field, 0)

	for _, f := range str.Fields.List {
		if len(f.Names) == 0 {
			// Check if this is a parent struct.
			ident, ok := f.Type.(*ast.Ident)
			if !ok {
				continue
			}

			typ, ok := ident.Obj.Decl.(*ast.TypeSpec)
			if !ok {
				continue
			}

			parentStr, ok := typ.Type.(*ast.StructType)
			if !ok {
				continue
			}

			parentFields, err := parseStruct(parentStr, kind)
			if err != nil {
				return nil, errors.Wrapf(err, "Failed to parse parent struct")
			}
			fields = append(fields, parentFields...)

			continue
		}

		if len(f.Names) != 1 {
			return nil, fmt.Errorf("Expected a single field name, got %q", f.Names)
		}

		field, err := parseField(f, kind)
		if err != nil {
			return nil, err
		}

		// Don't add field if it has been ignored.
		if field != nil {
			fields = append(fields, field)
		}
	}

	return fields, nil
}

func parseField(f *ast.Field, kind string) (*Field, error) {
	name := f.Names[0]

	if !name.IsExported() {
		//return nil, fmt.Errorf("Unexported field name %q", name.Name)
	}

	// Ignore fields that are marked with a tag of `db:"ingore"`
	if f.Tag != nil {
		tag := f.Tag.Value
		tagValue := reflect.StructTag(tag[1 : len(tag)-1]).Get("db")
		if tagValue == "ignore" {
			return nil, nil
		}
	}

	typeName := parseType(f.Type)
	if typeName == "" {
		return nil, fmt.Errorf("Unsupported type for field %q", name.Name)
	}

	typeObj := Type{
		Name: typeName,
	}

	if IsColumnType(typeName) {
		typeObj.Code = TypeColumn
	} else if strings.HasPrefix(typeName, "[]") {
		typeObj.Code = TypeSlice
	} else if strings.HasPrefix(typeName, "map[") {
		typeObj.Code = TypeMap
	} else {
		return nil, fmt.Errorf("Unsupported type for field %q", name.Name)
	}

	var config url.Values
	if f.Tag != nil {
		tag := f.Tag.Value
		var err error
		config, err = url.ParseQuery(reflect.StructTag(tag[1 : len(tag)-1]).Get("db"))
		if err != nil {
			return nil, errors.Wrap(err, "Parse 'db' structure tag")
		}
	}

	// Ignore fields that are marked with `db:"omit"`.
	if omit := config.Get("omit"); omit != "" {
		omitFields := strings.Split(omit, ",")
		stmtKind := strings.Replace(lex.Snake(kind), "_", "-", -1)
		switch kind {
		case "URIs":
			stmtKind = "names"
		case "GetMany":
			stmtKind = "objects"
		case "GetOne":
			stmtKind = "objects"
		case "DeleteMany":
			stmtKind = "delete"
		case "DeleteOne":
			stmtKind = "delete"
		}

		if shared.StringInSlice(kind, omitFields) || shared.StringInSlice(stmtKind, omitFields) {
			return nil, nil
		} else if kind == "exists" && shared.StringInSlice("id", omitFields) {
			// Exists checks ID, so if we are omitting the field from ID, also omit it from Exists.
			return nil, nil
		}
	}

	field := Field{
		Name:   name.Name,
		Type:   typeObj,
		Config: config,
	}

	return &field, nil
}

func parseType(x ast.Expr) string {
	switch t := x.(type) {
	case *ast.StarExpr:
		return parseType(t.X)
	case *ast.SelectorExpr:
		return parseType(t.X) + "." + t.Sel.String()
	case *ast.Ident:
		s := t.String()
		if s == "byte" {
			return "uint8"
		}
		return s
	case *ast.ArrayType:
		return "[" + parseType(t.Len) + "]" + parseType(t.Elt)
	case *ast.MapType:
		return "map[" + parseType(t.Key) + "]" + parseType(t.Value)
	case *ast.BasicLit:
		return t.Value
	case nil:
		return ""
	default:
		return ""
	}
}
<commit_msg>lxd/db/generate/db/parse: determine TableType on parse struct<commit_after>package db

import (
	"fmt"
	"go/ast"
	"net/url"
	"path/filepath"
	"reflect"
	"runtime"
	"sort"
	"strings"

	"github.com/lxc/lxd/lxd/db/generate/lex"
	"github.com/lxc/lxd/shared"
	"github.com/pkg/errors"
)

// Packages returns the the AST packages in which to search for structs.
//
// By default it includes the lxd/db and shared/api packages.
func Packages() (map[string]*ast.Package, error) {
	packages := map[string]*ast.Package{}

	_, filename, _, _ := runtime.Caller(0)

	for _, name := range defaultPackages {
		pkg, err := lex.Parse(filepath.Join(filepath.Dir(filename), "..", "..", "..", "..", name))
		if err != nil {
			return nil, errors.Wrapf(err, "Parse %q", name)
		}
		parts := strings.Split(name, "/")
		packages[parts[len(parts)-1]] = pkg
	}

	return packages, nil
}

var defaultPackages = []string{
	"shared/api",
	"lxd/db",
}

// FiltersFromStmt parses all filtering statement defined for the given entity. It
// returns all supported combinations of filters, sorted by number of criteria, and
// the corresponding set of unused filters from the Filter struct.
func FiltersFromStmt(pkg *ast.Package, kind string, entity string, filters []*Field) ([][]string, [][]string) {
	objects := pkg.Scope.Objects
	stmtFilters := [][]string{}

	prefix := fmt.Sprintf("%s%sBy", lex.Minuscule(lex.Camel(entity)), lex.Camel(kind))

	for name := range objects {
		if !strings.HasPrefix(name, prefix) {
			continue
		}
		rest := name[len(prefix):]
		stmtFilters = append(stmtFilters, strings.Split(rest, "And"))
	}

	stmtFilters = sortFilters(stmtFilters)
	ignoredFilters := [][]string{}

	for _, filterGroup := range stmtFilters {
		ignoredFilterGroup := []string{}
		for _, filter := range filters {
			if !shared.StringInSlice(filter.Name, filterGroup) {
				ignoredFilterGroup = append(ignoredFilterGroup, filter.Name)
			}
		}
		ignoredFilters = append(ignoredFilters, ignoredFilterGroup)
	}

	return stmtFilters, ignoredFilters
}

// RefFiltersFromStmt parses all filtering statement defined for the given entity reference.
func RefFiltersFromStmt(pkg *ast.Package, entity string, ref string, filters []*Field) ([][]string, [][]string) {
	objects := pkg.Scope.Objects
	stmtFilters := [][]string{}

	prefix := fmt.Sprintf("%s%sRefBy", lex.Minuscule(lex.Camel(entity)), lex.Capital(ref))

	for name := range objects {
		if !strings.HasPrefix(name, prefix) {
			continue
		}
		rest := name[len(prefix):]
		stmtFilters = append(stmtFilters, strings.Split(rest, "And"))
	}

	stmtFilters = sortFilters(stmtFilters)
	ignoredFilters := [][]string{}

	for _, filterGroup := range stmtFilters {
		ignoredFilterGroup := []string{}
		for _, filter := range filters {
			if !shared.StringInSlice(filter.Name, filterGroup) {
				ignoredFilterGroup = append(ignoredFilterGroup, filter.Name)
			}
		}
		ignoredFilters = append(ignoredFilters, ignoredFilterGroup)
	}

	return stmtFilters, ignoredFilters
}

func sortFilters(filters [][]string) [][]string {
	sort.Slice(filters, func(i, j int) bool {
		n1 := len(filters[i])
		n2 := len(filters[j])
		if n1 != n2 {
			return n1 > n2
		}
		f1 := sortFilter(filters[i])
		f2 := sortFilter(filters[j])
		for k := range f1 {
			if f1[k] == f2[k] {
				continue
			}
			return f1[k] > f2[k]
		}
		panic("duplicate filter")
	})
	return filters
}

func sortFilter(filter []string) []string {
	f := make([]string, len(filter))
	copy(f, filter)
	sort.Sort(sort.Reverse(sort.StringSlice(f)))
	return f
}

// Parse the structure declaration with the given name found in the given Go package.
// Any 'Entity' struct should also have an 'EntityFilter' struct defined in the same file.
func Parse(pkg *ast.Package, name string, kind string) (*Mapping, error) {
	// The main entity struct.
	str := findStruct(pkg.Scope, name)
	if str == nil {
		return nil, fmt.Errorf("No declaration found for %q", name)
	}

	fields, err := parseStruct(str, kind)
	if err != nil {
		return nil, errors.Wrapf(err, "Failed to parse %q", name)
	}

	m := &Mapping{
		Package: pkg.Name,
		Name:    name,
		Fields:  fields,
		Type:    tableType(pkg, name, fields),
	}

	// The 'EntityFilter' struct. This is used for filtering on specific fields of the entity.
	filterName := name + "Filter"
	filterStr := findStruct(pkg.Scope, filterName)
	if filterStr == nil {
		return nil, fmt.Errorf("No declaration found for %q", filterName)
	}

	filters, err := parseStruct(filterStr, kind)
	if err != nil {
		return nil, errors.Wrapf(err, "Failed to parse %q", name)
	}

	for i, filter := range filters {
		// Any field in EntityFilter must be present in the original struct.
		field := m.FieldByName(filter.Name)
		if field == nil {
			return nil, fmt.Errorf("Filter field %q is not in struct %q", filter.Name, name)
		}

		// Assign the config tags from the main entity struct to the Filter struct.
		filters[i].Config = field.Config

		// A Filter field and its indirect references must all be in the Filter struct.
		if field.IsIndirect() {
			indirectField := lex.Camel(field.Config.Get("via"))
			for i, f := range filters {
				if f.Name == indirectField {
					break
				}
				if i == len(filters)-1 {
					return nil, fmt.Errorf("Field %q requires field %q in struct %q", field.Name, indirectField, name+"Filter")
				}
			}
		}
	}

	m.Filters = filters

	return m, nil
}

// tableType determines the TableType for the given struct fields.
func tableType(pkg *ast.Package, name string, fields []*Field) TableType {
	fieldNames := FieldNames(fields)
	entities := strings.Split(lex.Snake(name), "_")
	if len(entities) == 2 {
		struct1 := findStruct(pkg.Scope, lex.Camel(lex.Singular(entities[0])))
		struct2 := findStruct(pkg.Scope, lex.Camel(lex.Singular(entities[1])))
		if struct1 != nil && struct2 != nil {
			return AssociationTable
		}
	}

	if shared.StringInSlice("ReferenceID", fieldNames) {
		if shared.StringInSlice("Key", fieldNames) && shared.StringInSlice("Value", fieldNames) {
			return MapTable
		}

		return ReferenceTable
	}

	return EntityTable
}

// Find the StructType node for the structure with the given name
func findStruct(scope *ast.Scope, name string) *ast.StructType {
	obj := scope.Lookup(name)
	if obj == nil {
		return nil
	}

	typ, ok := obj.Decl.(*ast.TypeSpec)
	if !ok {
		return nil
	}

	str, ok := typ.Type.(*ast.StructType)
	if !ok {
		return nil
	}

	return str
}

// Extract field information from the given structure.
func parseStruct(str *ast.StructType, kind string) ([]*Field, error) {
	fields := make([]*Field, 0)

	for _, f := range str.Fields.List {
		if len(f.Names) == 0 {
			// Check if this is a parent struct.
			ident, ok := f.Type.(*ast.Ident)
			if !ok {
				continue
			}

			typ, ok := ident.Obj.Decl.(*ast.TypeSpec)
			if !ok {
				continue
			}

			parentStr, ok := typ.Type.(*ast.StructType)
			if !ok {
				continue
			}

			parentFields, err := parseStruct(parentStr, kind)
			if err != nil {
				return nil, errors.Wrapf(err, "Failed to parse parent struct")
			}
			fields = append(fields, parentFields...)

			continue
		}

		if len(f.Names) != 1 {
			return nil, fmt.Errorf("Expected a single field name, got %q", f.Names)
		}

		field, err := parseField(f, kind)
		if err != nil {
			return nil, err
		}

		// Don't add field if it has been ignored.
		if field != nil {
			fields = append(fields, field)
		}
	}

	return fields, nil
}

func parseField(f *ast.Field, kind string) (*Field, error) {
	name := f.Names[0]

	if !name.IsExported() {
		//return nil, fmt.Errorf("Unexported field name %q", name.Name)
	}

	// Ignore fields that are marked with a tag of `db:"ingore"`
	if f.Tag != nil {
		tag := f.Tag.Value
		tagValue := reflect.StructTag(tag[1 : len(tag)-1]).Get("db")
		if tagValue == "ignore" {
			return nil, nil
		}
	}

	typeName := parseType(f.Type)
	if typeName == "" {
		return nil, fmt.Errorf("Unsupported type for field %q", name.Name)
	}

	typeObj := Type{
		Name: typeName,
	}

	if IsColumnType(typeName) {
		typeObj.Code = TypeColumn
	} else if strings.HasPrefix(typeName, "[]") {
		typeObj.Code = TypeSlice
	} else if strings.HasPrefix(typeName, "map[") {
		typeObj.Code = TypeMap
	} else {
		return nil, fmt.Errorf("Unsupported type for field %q", name.Name)
	}

	var config url.Values
	if f.Tag != nil {
		tag := f.Tag.Value
		var err error
		config, err = url.ParseQuery(reflect.StructTag(tag[1 : len(tag)-1]).Get("db"))
		if err != nil {
			return nil, errors.Wrap(err, "Parse 'db' structure tag")
		}
	}

	// Ignore fields that are marked with `db:"omit"`.
	if omit := config.Get("omit"); omit != "" {
		omitFields := strings.Split(omit, ",")
		stmtKind := strings.Replace(lex.Snake(kind), "_", "-", -1)
		switch kind {
		case "URIs":
			stmtKind = "names"
		case "GetMany":
			stmtKind = "objects"
		case "GetOne":
			stmtKind = "objects"
		case "DeleteMany":
			stmtKind = "delete"
		case "DeleteOne":
			stmtKind = "delete"
		}

		if shared.StringInSlice(kind, omitFields) || shared.StringInSlice(stmtKind, omitFields) {
			return nil, nil
		} else if kind == "exists" && shared.StringInSlice("id", omitFields) {
			// Exists checks ID, so if we are omitting the field from ID, also omit it from Exists.
			return nil, nil
		}
	}

	field := Field{
		Name:   name.Name,
		Type:   typeObj,
		Config: config,
	}

	return &field, nil
}

func parseType(x ast.Expr) string {
	switch t := x.(type) {
	case *ast.StarExpr:
		return parseType(t.X)
	case *ast.SelectorExpr:
		return parseType(t.X) + "." + t.Sel.String()
	case *ast.Ident:
		s := t.String()
		if s == "byte" {
			return "uint8"
		}
		return s
	case *ast.ArrayType:
		return "[" + parseType(t.Len) + "]" + parseType(t.Elt)
	case *ast.MapType:
		return "map[" + parseType(t.Key) + "]" + parseType(t.Value)
	case *ast.BasicLit:
		return t.Value
	case nil:
		return ""
	default:
		return ""
	}
}
<|endoftext|>


<commit_before>package network

import (
	"fmt"

	"github.com/lxc/lxd/lxd/cluster/request"
	"github.com/lxc/lxd/lxd/db"
	"github.com/lxc/lxd/lxd/revert"
	"github.com/lxc/lxd/shared/api"
	"github.com/lxc/lxd/shared/logger"
	"github.com/lxc/lxd/shared/validate"
)

// sriov represents a LXD sriov network.
type sriov struct {
	common
}

// Type returns the network type.
func (n *sriov) Type() string {
	return "sriov"
}

// DBType returns the network type DB ID.
func (n *sriov) DBType() db.NetworkType {
	return db.NetworkTypeSriov
}

// Validate network config.
func (n *sriov) Validate(config map[string]string) error {
	rules := map[string]func(value string) error{
		"parent":           validate.Required(validate.IsNotEmpty, validate.IsInterfaceName),
		"mtu":              validate.Optional(validate.IsNetworkMTU),
		"vlan":             validate.Optional(validate.IsNetworkVLAN),
		"maas.subnet.ipv4": validate.IsAny,
		"maas.subnet.ipv6": validate.IsAny,
	}

	err := n.validate(config, rules)
	if err != nil {
		return err
	}

	return nil
}

// Delete deletes a network.
func (n *sriov) Delete(clientType request.ClientType) error {
	n.logger.Debug("Delete", logger.Ctx{"clientType": clientType})

	return n.common.delete(clientType)
}

// Rename renames a network.
func (n *sriov) Rename(newName string) error {
	n.logger.Debug("Rename", logger.Ctx{"newName": newName})

	// Rename common steps.
	err := n.common.rename(newName)
	if err != nil {
		return err
	}

	return nil
}

// Start starts is a no-op.
func (n *sriov) Start() error {
	n.logger.Debug("Start")

	revert := revert.New()
	defer revert.Fail()

	revert.Add(func() { n.setUnavailable() })

	if !InterfaceExists(n.config["parent"]) {
		return fmt.Errorf("Parent interface %q not found", n.config["parent"])
	}

	return nil
}

// Stop stops is a no-op.
func (n *sriov) Stop() error {
	n.logger.Debug("Stop")

	return nil
}

// Update updates the network. Accepts notification boolean indicating if this update request is coming from a
// cluster notification, in which case do not update the database, just apply local changes needed.
func (n *sriov) Update(newNetwork api.NetworkPut, targetNode string, clientType request.ClientType) error {
	n.logger.Debug("Update", logger.Ctx{"clientType": clientType, "newNetwork": newNetwork})

	dbUpdateNeeeded, _, oldNetwork, err := n.common.configChanged(newNetwork)
	if err != nil {
		return err
	}

	if !dbUpdateNeeeded {
		return nil // Nothing changed.
	}

	// If the network as a whole has not had any previous creation attempts, or the node itself is still
	// pending, then don't apply the new settings to the node, just to the database record (ready for the
	// actual global create request to be initiated).
	if n.Status() == api.NetworkStatusPending || n.LocalStatus() == api.NetworkStatusPending {
		return n.common.update(newNetwork, targetNode, clientType)
	}

	revert := revert.New()
	defer revert.Fail()

	// Define a function which reverts everything.
	revert.Add(func() {
		// Reset changes to all nodes and database.
		n.common.update(oldNetwork, targetNode, clientType)
	})

	// Apply changes to all nodes and databse.
	err = n.common.update(newNetwork, targetNode, clientType)
	if err != nil {
		return err
	}

	revert.Success()
	return nil
}
<commit_msg>lxd/network/driver/sriov: Mark network as available on successful start<commit_after>package network

import (
	"fmt"

	"github.com/lxc/lxd/lxd/cluster/request"
	"github.com/lxc/lxd/lxd/db"
	"github.com/lxc/lxd/lxd/revert"
	"github.com/lxc/lxd/shared/api"
	"github.com/lxc/lxd/shared/logger"
	"github.com/lxc/lxd/shared/validate"
)

// sriov represents a LXD sriov network.
type sriov struct {
	common
}

// Type returns the network type.
func (n *sriov) Type() string {
	return "sriov"
}

// DBType returns the network type DB ID.
func (n *sriov) DBType() db.NetworkType {
	return db.NetworkTypeSriov
}

// Validate network config.
func (n *sriov) Validate(config map[string]string) error {
	rules := map[string]func(value string) error{
		"parent":           validate.Required(validate.IsNotEmpty, validate.IsInterfaceName),
		"mtu":              validate.Optional(validate.IsNetworkMTU),
		"vlan":             validate.Optional(validate.IsNetworkVLAN),
		"maas.subnet.ipv4": validate.IsAny,
		"maas.subnet.ipv6": validate.IsAny,
	}

	err := n.validate(config, rules)
	if err != nil {
		return err
	}

	return nil
}

// Delete deletes a network.
func (n *sriov) Delete(clientType request.ClientType) error {
	n.logger.Debug("Delete", logger.Ctx{"clientType": clientType})

	return n.common.delete(clientType)
}

// Rename renames a network.
func (n *sriov) Rename(newName string) error {
	n.logger.Debug("Rename", logger.Ctx{"newName": newName})

	// Rename common steps.
	err := n.common.rename(newName)
	if err != nil {
		return err
	}

	return nil
}

// Start starts is a no-op.
func (n *sriov) Start() error {
	n.logger.Debug("Start")

	revert := revert.New()
	defer revert.Fail()

	revert.Add(func() { n.setUnavailable() })

	if !InterfaceExists(n.config["parent"]) {
		return fmt.Errorf("Parent interface %q not found", n.config["parent"])
	}

	revert.Success()

	// Ensure network is marked as available now its started.
	n.setAvailable()

	return nil
}

// Stop stops is a no-op.
func (n *sriov) Stop() error {
	n.logger.Debug("Stop")

	return nil
}

// Update updates the network. Accepts notification boolean indicating if this update request is coming from a
// cluster notification, in which case do not update the database, just apply local changes needed.
func (n *sriov) Update(newNetwork api.NetworkPut, targetNode string, clientType request.ClientType) error {
	n.logger.Debug("Update", logger.Ctx{"clientType": clientType, "newNetwork": newNetwork})

	dbUpdateNeeeded, _, oldNetwork, err := n.common.configChanged(newNetwork)
	if err != nil {
		return err
	}

	if !dbUpdateNeeeded {
		return nil // Nothing changed.
	}

	// If the network as a whole has not had any previous creation attempts, or the node itself is still
	// pending, then don't apply the new settings to the node, just to the database record (ready for the
	// actual global create request to be initiated).
	if n.Status() == api.NetworkStatusPending || n.LocalStatus() == api.NetworkStatusPending {
		return n.common.update(newNetwork, targetNode, clientType)
	}

	revert := revert.New()
	defer revert.Fail()

	// Define a function which reverts everything.
	revert.Add(func() {
		// Reset changes to all nodes and database.
		n.common.update(oldNetwork, targetNode, clientType)
	})

	// Apply changes to all nodes and databse.
	err = n.common.update(newNetwork, targetNode, clientType)
	if err != nil {
		return err
	}

	revert.Success()
	return nil
}
<|endoftext|>


<commit_before>package main

import (
	"encoding/csv"
	"flag"
	"io"
	"log"
	"net/http"
	"strconv"
	"sync"
	"time"

	"github.com/prometheus/client_golang/prometheus"
)

const expectedCsvFieldCount = 52

var (
	totalScrapes     = prometheus.NewCounter()
	scrapeFailures   = prometheus.NewCounter()
	csvParseFailures = prometheus.NewCounter()
)

type registry struct {
	prometheus.Registry
	serviceMetrics map[int]prometheus.Gauge
	backendMetrics map[int]prometheus.Gauge
}

func newRegistry() *registry {
	r := &registry{prometheus.NewRegistry(), make(map[int]prometheus.Gauge), make(map[int]prometheus.Gauge)}

	r.Register("haproxy_exporter_total_scrapes", "Current total HAProxy scrapes.", prometheus.NilLabels, totalScrapes)
	r.Register("haproxy_exporter_scrape_failures", "Number of errors while scraping HAProxy.", prometheus.NilLabels, scrapeFailures)
	r.Register("haproxy_exporter_csv_parse_failures", "Number of errors while parsing CSV.", prometheus.NilLabels, csvParseFailures)

	r.serviceMetrics = map[int]prometheus.Gauge{
		2: r.newGauge("haproxy_current_queue", "Current server queue length."),
		3: r.newGauge("haproxy_max_queue", "Maximum server queue length."),
	}

	r.backendMetrics = map[int]prometheus.Gauge{
		4:  r.newGauge("haproxy_current_sessions", "Current number of active sessions."),
		5:  r.newGauge("haproxy_max_sessions", "Maximum number of active sessions."),
		8:  r.newGauge("haproxy_bytes_in", "Current total of incoming bytes."),
		9:  r.newGauge("haproxy_bytes_out", "Current total of outgoing bytes."),
		17: r.newGauge("haproxy_instance_up", "Current health status of the instance (1 = UP, 0 = DOWN)."),
		33: r.newGauge("haproxy_current_session_rate", "Current number of sessions per second."),
		35: r.newGauge("haproxy_max_session_rate", "Maximum number of sessions per second."),
	}

	return r
}

func (r *registry) newGauge(metricName string, docString string) prometheus.Gauge {
	gauge := prometheus.NewGauge()
	r.Register(metricName, docString, prometheus.NilLabels, gauge)
	return gauge
}

// Exporter collects HAProxy stats from the given URI and exports them using
// the prometheus metrics package.
type Exporter struct {
	URI   string
	reg   *registry
	mutex sync.RWMutex
}

// NewExporter returns an initialized Exporter.
func NewExporter(uri string) *Exporter {
	return &Exporter{
		URI: uri,
		reg: newRegistry(),
	}
}

// Registry returns a prometheus.Registry type with the complete state of the
// last stats collection run.
func (e *Exporter) Registry() prometheus.Registry {
	e.mutex.RLock()
	defer e.mutex.RUnlock()

	return e.reg
}

// Handler returns a http.HandlerFunc of the last finished prometheus.Registry.
func (e *Exporter) Handler() http.HandlerFunc {
	return func(w http.ResponseWriter, r *http.Request) {
		reg := e.Registry()
		f := reg.Handler()

		f(w, r)
	}
}

// Scrape fetches the stats from configured HAProxy location. It creates a new
// prometheus.Registry object every time to not leak stale data from previous
// collections.
func (e *Exporter) Scrape() {
	csvRows := make(chan []string)
	quitChan := make(chan bool)
	reg := newRegistry()

	go e.scrape(csvRows, quitChan)
	reg.exportMetrics(csvRows, quitChan)

	e.mutex.Lock()
	defer e.mutex.Unlock()

	e.reg = reg
}

// ScrapePeriodically runs the Scrape function in the specified interval.
func (e *Exporter) ScrapePeriodically(interval time.Duration) {
	ticker := time.NewTicker(interval)
	defer ticker.Stop()

	for _ = range ticker.C {
		e.Scrape()
	}
}

func (e *Exporter) scrape(csvRows chan []string, quitChan chan bool) {
	defer close(quitChan)
	defer totalScrapes.Increment(prometheus.NilLabels)

	resp, err := http.Get(e.URI)
	if err != nil {
		log.Printf("Error while scraping HAProxy: %v", err)
		scrapeFailures.Increment(prometheus.NilLabels)
		return
	}
	defer resp.Body.Close()

	reader := csv.NewReader(resp.Body)
	reader.TrailingComma = true
	reader.Comment = '#'

	for {
		row, err := reader.Read()

		if err == io.EOF {
			break
		}
		if err != nil {
			log.Printf("Error while reading CSV: %v", err)
			csvParseFailures.Increment(prometheus.NilLabels)
			return
		}
		if len(row) == 0 {
			continue
		}

		csvRows <- row
	}

}

func (r *registry) exportMetrics(csvRows chan []string, quitChan chan bool) {
	for {
		select {
		case row := <-csvRows:
			r.exportCsvRow(row)
		case <-quitChan:
			return
		}
	}
}

func (r *registry) exportCsvRow(csvRow []string) {
	if len(csvRow) != expectedCsvFieldCount {
		log.Printf("Wrong CSV field count: %d vs. %d", len(csvRow), expectedCsvFieldCount)
		csvParseFailures.Increment(prometheus.NilLabels)
		return
	}

	service, instance := csvRow[0], csvRow[1]

	if instance == "FRONTEND" {
		return
	}

	if instance == "BACKEND" {
		labels := map[string]string{
			"service": service,
		}

		exportCsvFields(labels, r.serviceMetrics, csvRow)
	} else {
		labels := map[string]string{
			"service":  service,
			"instance": instance,
		}

		exportCsvFields(labels, r.backendMetrics, csvRow)
	}
}

func exportCsvFields(labels map[string]string, fields map[int]prometheus.Gauge, csvRow []string) {
	for fieldIdx, gauge := range fields {
		valueStr := csvRow[fieldIdx]
		if valueStr == "" {
			continue
		}

		var value int64
		var err error
		switch valueStr {
		// UP or UP going down
		case "UP", "UP 1/3", "UP 2/3":
			value = 1
		// DOWN or DOWN going up
		case "DOWN", "DOWN 1/2":
			value = 0
		case "OPEN":
			value = 0
		default:
			value, err = strconv.ParseInt(valueStr, 10, 64)
			if err != nil {
				log.Printf("Error while parsing CSV field value %s: %v", valueStr, err)
				csvParseFailures.Increment(prometheus.NilLabels)
				continue
			}
		}
		gauge.Set(labels, float64(value))
	}
}

func main() {
	var (
		listeningAddress      = flag.String("telemetry.address", ":8080", "Address on which to expose JSON metrics.")
		metricsEndpoint       = flag.String("telemetry.endpoint", prometheus.ExpositionResource, "Path under which to expose metrics.")
		haProxyScrapeUri      = flag.String("haproxy.scrape_uri", "http://localhost/;csv", "URI on which to scrape HAProxy.")
		haProxyScrapeInterval = flag.Duration("haproxy.scrape_interval", 15 * time.Second, "Interval in seconds between scrapes.")
	)
	flag.Parse()

	exporter := NewExporter(*haProxyScrapeUri)
	go exporter.ScrapePeriodically(*haProxyScrapeInterval)

	log.Printf("Starting Server: %s", *listeningAddress)
	http.Handle(*metricsEndpoint, exporter.Handler())
	log.Fatal(http.ListenAndServe(*listeningAddress, nil))
}
<commit_msg>Consolidate label naming<commit_after>package main

import (
	"encoding/csv"
	"flag"
	"io"
	"log"
	"net/http"
	"strconv"
	"sync"
	"time"

	"github.com/prometheus/client_golang/prometheus"
)

const expectedCsvFieldCount = 52

var (
	totalScrapes     = prometheus.NewCounter()
	scrapeFailures   = prometheus.NewCounter()
	csvParseFailures = prometheus.NewCounter()
)

type registry struct {
	prometheus.Registry
	serviceMetrics map[int]prometheus.Gauge
	backendMetrics map[int]prometheus.Gauge
}

func newRegistry() *registry {
	r := &registry{prometheus.NewRegistry(), make(map[int]prometheus.Gauge), make(map[int]prometheus.Gauge)}

	r.Register("haproxy_exporter_total_scrapes", "Current total HAProxy scrapes.", prometheus.NilLabels, totalScrapes)
	r.Register("haproxy_exporter_scrape_failures", "Number of errors while scraping HAProxy.", prometheus.NilLabels, scrapeFailures)
	r.Register("haproxy_exporter_csv_parse_failures", "Number of errors while parsing CSV.", prometheus.NilLabels, csvParseFailures)

	r.serviceMetrics = map[int]prometheus.Gauge{
		2: r.newGauge("haproxy_current_queue", "Current server queue length."),
		3: r.newGauge("haproxy_max_queue", "Maximum server queue length."),
	}

	r.backendMetrics = map[int]prometheus.Gauge{
		4:  r.newGauge("haproxy_current_sessions", "Current number of active sessions."),
		5:  r.newGauge("haproxy_max_sessions", "Maximum number of active sessions."),
		8:  r.newGauge("haproxy_bytes_in", "Current total of incoming bytes."),
		9:  r.newGauge("haproxy_bytes_out", "Current total of outgoing bytes."),
		17: r.newGauge("haproxy_server_up", "Current health status of the server (1 = UP, 0 = DOWN)."),
		33: r.newGauge("haproxy_current_session_rate", "Current number of sessions per second."),
		35: r.newGauge("haproxy_max_session_rate", "Maximum number of sessions per second."),
	}

	return r
}

func (r *registry) newGauge(metricName string, docString string) prometheus.Gauge {
	gauge := prometheus.NewGauge()
	r.Register(metricName, docString, prometheus.NilLabels, gauge)
	return gauge
}

// Exporter collects HAProxy stats from the given URI and exports them using
// the prometheus metrics package.
type Exporter struct {
	URI   string
	reg   *registry
	mutex sync.RWMutex
}

// NewExporter returns an initialized Exporter.
func NewExporter(uri string) *Exporter {
	return &Exporter{
		URI: uri,
		reg: newRegistry(),
	}
}

// Registry returns a prometheus.Registry type with the complete state of the
// last stats collection run.
func (e *Exporter) Registry() prometheus.Registry {
	e.mutex.RLock()
	defer e.mutex.RUnlock()

	return e.reg
}

// Handler returns a http.HandlerFunc of the last finished prometheus.Registry.
func (e *Exporter) Handler() http.HandlerFunc {
	return func(w http.ResponseWriter, r *http.Request) {
		reg := e.Registry()
		f := reg.Handler()

		f(w, r)
	}
}

// Scrape fetches the stats from configured HAProxy location. It creates a new
// prometheus.Registry object every time to not leak stale data from previous
// collections.
func (e *Exporter) Scrape() {
	csvRows := make(chan []string)
	quitChan := make(chan bool)
	reg := newRegistry()

	go e.scrape(csvRows, quitChan)
	reg.exportMetrics(csvRows, quitChan)

	e.mutex.Lock()
	defer e.mutex.Unlock()

	e.reg = reg
}

// ScrapePeriodically runs the Scrape function in the specified interval.
func (e *Exporter) ScrapePeriodically(interval time.Duration) {
	ticker := time.NewTicker(interval)
	defer ticker.Stop()

	for _ = range ticker.C {
		e.Scrape()
	}
}

func (e *Exporter) scrape(csvRows chan []string, quitChan chan bool) {
	defer close(quitChan)
	defer totalScrapes.Increment(prometheus.NilLabels)

	resp, err := http.Get(e.URI)
	if err != nil {
		log.Printf("Error while scraping HAProxy: %v", err)
		scrapeFailures.Increment(prometheus.NilLabels)
		return
	}
	defer resp.Body.Close()

	reader := csv.NewReader(resp.Body)
	reader.TrailingComma = true
	reader.Comment = '#'

	for {
		row, err := reader.Read()

		if err == io.EOF {
			break
		}
		if err != nil {
			log.Printf("Error while reading CSV: %v", err)
			csvParseFailures.Increment(prometheus.NilLabels)
			return
		}
		if len(row) == 0 {
			continue
		}

		csvRows <- row
	}

}

func (r *registry) exportMetrics(csvRows chan []string, quitChan chan bool) {
	for {
		select {
		case row := <-csvRows:
			r.exportCsvRow(row)
		case <-quitChan:
			return
		}
	}
}

func (r *registry) exportCsvRow(csvRow []string) {
	if len(csvRow) != expectedCsvFieldCount {
		log.Printf("Wrong CSV field count: %d vs. %d", len(csvRow), expectedCsvFieldCount)
		csvParseFailures.Increment(prometheus.NilLabels)
		return
	}

	service, server := csvRow[0], csvRow[1]

	if server == "FRONTEND" {
		return
	}

	if server == "BACKEND" {
		labels := map[string]string{
			"service": service,
		}

		exportCsvFields(labels, r.serviceMetrics, csvRow)
	} else {
		labels := map[string]string{
			"service": service,
			"server":  server,
		}

		exportCsvFields(labels, r.backendMetrics, csvRow)
	}
}

func exportCsvFields(labels map[string]string, fields map[int]prometheus.Gauge, csvRow []string) {
	for fieldIdx, gauge := range fields {
		valueStr := csvRow[fieldIdx]
		if valueStr == "" {
			continue
		}

		var value int64
		var err error
		switch valueStr {
		// UP or UP going down
		case "UP", "UP 1/3", "UP 2/3":
			value = 1
		// DOWN or DOWN going up
		case "DOWN", "DOWN 1/2":
			value = 0
		case "OPEN":
			value = 0
		default:
			value, err = strconv.ParseInt(valueStr, 10, 64)
			if err != nil {
				log.Printf("Error while parsing CSV field value %s: %v", valueStr, err)
				csvParseFailures.Increment(prometheus.NilLabels)
				continue
			}
		}
		gauge.Set(labels, float64(value))
	}
}

func main() {
	var (
		listeningAddress      = flag.String("telemetry.address", ":8080", "Address on which to expose JSON metrics.")
		metricsEndpoint       = flag.String("telemetry.endpoint", prometheus.ExpositionResource, "Path under which to expose metrics.")
		haProxyScrapeUri      = flag.String("haproxy.scrape_uri", "http://localhost/;csv", "URI on which to scrape HAProxy.")
		haProxyScrapeInterval = flag.Duration("haproxy.scrape_interval", 15*time.Second, "Interval in seconds between scrapes.")
	)
	flag.Parse()

	exporter := NewExporter(*haProxyScrapeUri)
	go exporter.ScrapePeriodically(*haProxyScrapeInterval)

	log.Printf("Starting Server: %s", *listeningAddress)
	http.Handle(*metricsEndpoint, exporter.Handler())
	log.Fatal(http.ListenAndServe(*listeningAddress, nil))
}
<|endoftext|>


<commit_before>// Copyright 2017 by caixw, All rights reserved.
// Use of this source code is governed by a MIT
// license that can be found in the LICENSE file.

package helper

import (
	"bytes"
	"encoding/xml"
	"strings"
)

// XMLWriter XML 操作类，简单地封装 bytes.Buffer。
type XMLWriter struct {
	buf    *bytes.Buffer
	err    error // 缓存 buf.Write* 系列函数的错误信息，并阻止其再次执行
	indent int   // 保存当前的缩进量
}

// NewWriter 声明一个新的 XMLWriter
func NewWriter() *XMLWriter {
	w := &XMLWriter{
		buf: bytes.NewBufferString(xml.Header),
	}

	return w
}

func (w *XMLWriter) writeString(str string) {
	if w.err != nil {
		return
	}

	_, w.err = w.buf.WriteString(str)
}

func (w *XMLWriter) writeByte(b byte) {
	if w.err != nil {
		return
	}

	w.err = w.buf.WriteByte(b)
}

// WriteStartElement 写入一个开始元素
func (w *XMLWriter) WriteStartElement(name string, attr map[string]string) {
	w.startElement(name, attr, true)
}

// newline 是否换行
func (w *XMLWriter) startElement(name string, attr map[string]string, newline bool) {
	w.writeString(strings.Repeat(" ", w.indent*4))
	w.indent++

	w.writeByte('<')
	w.writeString(name)
	w.writeAttr(attr)
	w.writeByte('>')

	if newline {
		w.writeByte('\n')
	}
}

// WriteEndElement 写入一个结束元素
func (w *XMLWriter) WriteEndElement(name string) {
	w.endElement(name, true)
}

// indent 是否需要填上缩进时的字符，如果不换行输出结束符，则不能输出缩进字符串
func (w *XMLWriter) endElement(name string, indent bool) {
	w.indent--
	if indent {
		w.writeString(strings.Repeat(" ", w.indent*4))
	}

	w.writeString("</")
	w.writeString(name)
	w.writeByte('>')

	w.writeByte('\n')
}

// WriteCloseElement 写入一个自闭合的元素
// name 元素标签名；
// attr 元素的属性。
func (w *XMLWriter) WriteCloseElement(name string, attr map[string]string) {
	w.writeString(strings.Repeat(" ", w.indent*4))

	w.writeByte('<')
	w.writeString(name)
	w.writeAttr(attr)
	w.writeString(" />")

	w.writeByte('\n')
}

// WriteElement 写入一个完整的元素。
// name 元素标签名；
// val 元素内容；
// attr 元素的属性。
func (w *XMLWriter) WriteElement(name, val string, attr map[string]string) {
	w.startElement(name, attr, false)
	w.writeString(val)
	w.endElement(name, false)
}

// WritePI 写入一个 PI 指令
func (w *XMLWriter) WritePI(name string, kv map[string]string) {
	w.writeString("<?")
	w.writeString(name)
	w.writeAttr(kv)
	w.writeString("?>")

	w.writeByte('\n')
}

func (w *XMLWriter) writeAttr(attr map[string]string) {
	for k, v := range attr {
		w.writeByte(' ')
		w.writeString(k)
		w.writeString(`="`)
		w.writeString(v)
		w.writeByte('"')
	}
}

// Bytes 将内容转换成 []byte 并返回
func (w *XMLWriter) Bytes() ([]byte, error) {
	if w.err != nil {
		return nil, w.err
	}

	return w.buf.Bytes(), nil
}
<commit_msg>使用一个常量代替 xml 的缩进量<commit_after>// Copyright 2017 by caixw, All rights reserved.
// Use of this source code is governed by a MIT
// license that can be found in the LICENSE file.

package helper

import (
	"bytes"
	"encoding/xml"
	"strings"
)

// xml 中每次的缩进量
const indentWidth = 4

// XMLWriter XML 操作类，简单地封装 bytes.Buffer。
type XMLWriter struct {
	buf    *bytes.Buffer
	err    error // 缓存 buf.Write* 系列函数的错误信息，并阻止其再次执行
	indent int   // 保存当前的缩进量
}

// NewWriter 声明一个新的 XMLWriter
func NewWriter() *XMLWriter {
	w := &XMLWriter{
		buf: bytes.NewBufferString(xml.Header),
	}

	return w
}

func (w *XMLWriter) writeString(str string) {
	if w.err != nil {
		return
	}

	_, w.err = w.buf.WriteString(str)
}

func (w *XMLWriter) writeByte(b byte) {
	if w.err != nil {
		return
	}

	w.err = w.buf.WriteByte(b)
}

func (w *XMLWriter) writeIndent() {
	w.writeString(strings.Repeat(" ", w.indent*indentWidth))
}

// WriteStartElement 写入一个开始元素
func (w *XMLWriter) WriteStartElement(name string, attr map[string]string) {
	w.startElement(name, attr, true)
}

// newline 是否换行
func (w *XMLWriter) startElement(name string, attr map[string]string, newline bool) {
	w.writeIndent()
	w.indent++

	w.writeByte('<')
	w.writeString(name)
	w.writeAttr(attr)
	w.writeByte('>')

	if newline {
		w.writeByte('\n')
	}
}

// WriteEndElement 写入一个结束元素
func (w *XMLWriter) WriteEndElement(name string) {
	w.endElement(name, true)
}

// indent 是否需要填上缩进时的字符，如果不换行输出结束符，则不能输出缩进字符串
func (w *XMLWriter) endElement(name string, indent bool) {
	w.indent--
	if indent {
		w.writeIndent()
	}

	w.writeString("</")
	w.writeString(name)
	w.writeByte('>')

	w.writeByte('\n')
}

// WriteCloseElement 写入一个自闭合的元素
// name 元素标签名；
// attr 元素的属性。
func (w *XMLWriter) WriteCloseElement(name string, attr map[string]string) {
	w.writeIndent()

	w.writeByte('<')
	w.writeString(name)
	w.writeAttr(attr)
	w.writeString(" />")

	w.writeByte('\n')
}

// WriteElement 写入一个完整的元素。
// name 元素标签名；
// val 元素内容；
// attr 元素的属性。
func (w *XMLWriter) WriteElement(name, val string, attr map[string]string) {
	w.startElement(name, attr, false)
	w.writeString(val)
	w.endElement(name, false)
}

// WritePI 写入一个 PI 指令
func (w *XMLWriter) WritePI(name string, kv map[string]string) {
	w.writeString("<?")
	w.writeString(name)
	w.writeAttr(kv)
	w.writeString("?>")

	w.writeByte('\n')
}

func (w *XMLWriter) writeAttr(attr map[string]string) {
	for k, v := range attr {
		w.writeByte(' ')
		w.writeString(k)
		w.writeString(`="`)
		w.writeString(v)
		w.writeByte('"')
	}
}

// Bytes 将内容转换成 []byte 并返回
func (w *XMLWriter) Bytes() ([]byte, error) {
	if w.err != nil {
		return nil, w.err
	}

	return w.buf.Bytes(), nil
}
<|endoftext|>


<commit_before>//
// Copyright 2014-2017 Cristian Maglie. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
//

package enumerator // import "go.bug.st/serial.v1/enumerator"

// #cgo LDFLAGS: -framework CoreFoundation -framework IOKit -fconstant-cfstrings
// #include <IOKit/IOKitLib.h>
// #include <CoreFoundation/CoreFoundation.h>
// #include <stdlib.h>
import "C"
import (
	"errors"
	"fmt"
	"unsafe"
)

func nativeGetDetailedPortsList() ([]*PortDetails, error) {
	var ports []*PortDetails

	services, err := getAllServices("IOSerialBSDClient")
	if err != nil {
		return nil, &PortError{code: ErrorEnumeratingPorts, causedBy: err}
	}
	for _, service := range services {
		defer service.Release()

		port, err := extractPortInfo(C.io_registry_entry_t(service))
		if err != nil {
			return nil, &PortError{code: ErrorEnumeratingPorts, causedBy: err}
		}
		ports = append(ports, port)
	}
	return ports, nil
}

func extractPortInfo(service C.io_registry_entry_t) (*PortDetails, error) {
	name, err := service.GetStringProperty("IOCalloutDevice")
	if err != nil {
		return nil, fmt.Errorf("Error extracting port info from device: %s", err.Error())
	}
	port := &PortDetails{}
	port.Name = name
	port.IsUSB = false

	usbDevice := service
	for usbDevice.GetClass() != "IOUSBDevice" {
		if usbDevice, err = usbDevice.GetParent("IOService"); err != nil {
			break
		}
	}
	if err == nil {
		// It's an IOUSBDevice
		vid, _ := usbDevice.GetIntProperty("idVendor", C.kCFNumberSInt16Type)
		pid, _ := usbDevice.GetIntProperty("idProduct", C.kCFNumberSInt16Type)
		serialNumber, _ := usbDevice.GetStringProperty("USB Serial Number")
		//product, _ := usbDevice.GetStringProperty("USB Product Name")
		//manufacturer, _ := usbDevice.GetStringProperty("USB Vendor Name")
		//fmt.Println(product + " - " + manufacturer)

		port.IsUSB = true
		port.VID = fmt.Sprintf("%04X", vid)
		port.PID = fmt.Sprintf("%04X", pid)
		port.SerialNumber = serialNumber
	}
	return port, nil
}

func getAllServices(serviceType string) ([]C.io_object_t, error) {
	i, err := getMatchingServices(serviceMatching(serviceType))
	if err != nil {
		return nil, err
	}
	defer i.Release()

	var services []C.io_object_t
	tries := 0
	for tries < 5 {
		// Extract all elements from iterator
		if service, ok := i.Next(); ok {
			services = append(services, service)
			continue
		}
		// If iterator is still valid return the result
		if i.IsValid() {
			return services, nil
		}
		// Otherwise empty the result and retry
		for _, s := range services {
			s.Release()
		}
		services = []C.io_object_t{}
		i.Reset()
		tries++
	}
	// Give up if the iteration continues to fail...
	return nil, fmt.Errorf("IOServiceGetMatchingServices failed, data changed while iterating")
}

// serviceMatching create a matching dictionary that specifies an IOService class match.
func serviceMatching(serviceType string) C.CFMutableDictionaryRef {
	t := C.CString(serviceType)
	defer C.free(unsafe.Pointer(t))
	return C.IOServiceMatching(t)
}

// getMatchingServices look up registered IOService objects that match a matching dictionary.
func getMatchingServices(matcher C.CFMutableDictionaryRef) (C.io_iterator_t, error) {
	var i C.io_iterator_t
	err := C.IOServiceGetMatchingServices(C.kIOMasterPortDefault, matcher, &i)
	if err != C.KERN_SUCCESS {
		return 0, fmt.Errorf("IOServiceGetMatchingServices failed (code %d)", err)
	}
	return i, nil
}

// CFStringRef

func cfStringCreateWithString(s string) C.CFStringRef {
	c := C.CString(s)
	defer C.free(unsafe.Pointer(c))
	return C.CFStringCreateWithCString(
		C.kCFAllocatorDefault, c, C.kCFStringEncodingMacRoman)
}

// io_registry_entry_t

func (me *C.io_registry_entry_t) GetParent(plane string) (C.io_registry_entry_t, error) {
	cPlane := C.CString(plane)
	defer C.free(unsafe.Pointer(cPlane))
	var parent C.io_registry_entry_t
	err := C.IORegistryEntryGetParentEntry(*me, cPlane, &parent)
	if err != 0 {
		return 0, errors.New("No parent device available")
	}
	return parent, nil
}

func (me *C.io_registry_entry_t) GetClass() string {
	obj := (*C.io_object_t)(me)
	return obj.GetClass()
}

func (me *C.io_registry_entry_t) GetStringProperty(key string) (string, error) {
	k := cfStringCreateWithString(key)
	defer C.CFRelease(C.CFTypeRef(k))
	property := C.IORegistryEntryCreateCFProperty(*me, k, C.kCFAllocatorDefault, 0)
	if property == nil {
		return "", errors.New("Property not found: " + key)
	}
	defer C.CFRelease(property)

	if ptr := C.CFStringGetCStringPtr(property, 0); ptr != nil {
		return C.GoString(ptr), nil
	}
	// in certain circumstances CFStringGetCStringPtr may return NULL
	// and we must retrieve the string by copy
	buff := make([]C.char, 1024)
	if C.CFStringGetCString(property, &buff[0], 1024, 0) != C.true {
		return "", fmt.Errorf("Property '%s' can't be converted", key)
	}
	return C.GoString(&buff[0]), nil
}

func (me *C.io_registry_entry_t) GetIntProperty(key string, intType C.CFNumberType) (int, error) {
	k := cfStringCreateWithString(key)
	defer C.CFRelease(C.CFTypeRef(k))
	property := C.IORegistryEntryCreateCFProperty(*me, k, C.kCFAllocatorDefault, 0)
	if property == nil {
		return 0, errors.New("Property not found: " + key)
	}
	defer C.CFRelease(property)
	var res int
	if C.CFNumberGetValue(property, intType, unsafe.Pointer(&res)) != C.true {
		return res, fmt.Errorf("Property '%s' can't be converted or has been truncated", key)
	}
	return res, nil
}

// io_iterator_t

// IsValid checks if an iterator is still valid.
// Some iterators will be made invalid if changes are made to the
// structure they are iterating over. This function checks the iterator
// is still valid and should be called when Next returns zero.
// An invalid iterator can be Reset and the iteration restarted.
func (me *C.io_iterator_t) IsValid() bool {
	return C.IOIteratorIsValid(*me) == C.true
}

func (me *C.io_iterator_t) Reset() {
	C.IOIteratorReset(*me)
}

func (me *C.io_iterator_t) Next() (C.io_object_t, bool) {
	res := C.IOIteratorNext(*me)
	return res, res != 0
}

func (me *C.io_iterator_t) Release() {
	C.IOObjectRelease(C.io_object_t(*me))
}

// io_object_t

func (me *C.io_object_t) Release() {
	C.IOObjectRelease(*me)
}

func (me *C.io_object_t) GetClass() string {
	class := make([]C.char, 1024)
	C.IOObjectGetClass(*me, &class[0])
	return C.GoString(&class[0])
}
<commit_msg>Fix build using Darwin and osxcross<commit_after>//
// Copyright 2014-2017 Cristian Maglie. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
//

package enumerator // import "go.bug.st/serial.v1/enumerator"

// #cgo LDFLAGS: -framework CoreFoundation -framework IOKit -fconstant-cfstrings
// #include <IOKit/IOKitLib.h>
// #include <CoreFoundation/CoreFoundation.h>
// #include <stdlib.h>
import "C"
import (
	"errors"
	"fmt"
	"unsafe"
)

func nativeGetDetailedPortsList() ([]*PortDetails, error) {
	var ports []*PortDetails

	services, err := getAllServices("IOSerialBSDClient")
	if err != nil {
		return nil, &PortEnumerationError{causedBy: err}
	}
	for _, service := range services {
		defer service.Release()

		port, err := extractPortInfo(C.io_registry_entry_t(service))
		if err != nil {
			return nil, &PortEnumerationError{causedBy: err}
		}
		ports = append(ports, port)
	}
	return ports, nil
}

func extractPortInfo(service C.io_registry_entry_t) (*PortDetails, error) {
	name, err := service.GetStringProperty("IOCalloutDevice")
	if err != nil {
		return nil, fmt.Errorf("Error extracting port info from device: %s", err.Error())
	}
	port := &PortDetails{}
	port.Name = name
	port.IsUSB = false

	usbDevice := service
	for usbDevice.GetClass() != "IOUSBDevice" {
		if usbDevice, err = usbDevice.GetParent("IOService"); err != nil {
			break
		}
	}
	if err == nil {
		// It's an IOUSBDevice
		vid, _ := usbDevice.GetIntProperty("idVendor", C.kCFNumberSInt16Type)
		pid, _ := usbDevice.GetIntProperty("idProduct", C.kCFNumberSInt16Type)
		serialNumber, _ := usbDevice.GetStringProperty("USB Serial Number")
		//product, _ := usbDevice.GetStringProperty("USB Product Name")
		//manufacturer, _ := usbDevice.GetStringProperty("USB Vendor Name")
		//fmt.Println(product + " - " + manufacturer)

		port.IsUSB = true
		port.VID = fmt.Sprintf("%04X", vid)
		port.PID = fmt.Sprintf("%04X", pid)
		port.SerialNumber = serialNumber
	}
	return port, nil
}

func getAllServices(serviceType string) ([]C.io_object_t, error) {
	i, err := getMatchingServices(serviceMatching(serviceType))
	if err != nil {
		return nil, err
	}
	defer i.Release()

	var services []C.io_object_t
	tries := 0
	for tries < 5 {
		// Extract all elements from iterator
		if service, ok := i.Next(); ok {
			services = append(services, service)
			continue
		}
		// If iterator is still valid return the result
		if i.IsValid() {
			return services, nil
		}
		// Otherwise empty the result and retry
		for _, s := range services {
			s.Release()
		}
		services = []C.io_object_t{}
		i.Reset()
		tries++
	}
	// Give up if the iteration continues to fail...
	return nil, fmt.Errorf("IOServiceGetMatchingServices failed, data changed while iterating")
}

// serviceMatching create a matching dictionary that specifies an IOService class match.
func serviceMatching(serviceType string) C.CFMutableDictionaryRef {
	t := C.CString(serviceType)
	defer C.free(unsafe.Pointer(t))
	return C.IOServiceMatching(t)
}

// getMatchingServices look up registered IOService objects that match a matching dictionary.
func getMatchingServices(matcher C.CFMutableDictionaryRef) (C.io_iterator_t, error) {
	var i C.io_iterator_t
	err := C.IOServiceGetMatchingServices(C.kIOMasterPortDefault, matcher, &i)
	if err != C.KERN_SUCCESS {
		return 0, fmt.Errorf("IOServiceGetMatchingServices failed (code %d)", err)
	}
	return i, nil
}

// CFStringRef

func cfStringCreateWithString(s string) C.CFStringRef {
	c := C.CString(s)
	defer C.free(unsafe.Pointer(c))
	return C.CFStringCreateWithCString(
		C.kCFAllocatorDefault, c, C.kCFStringEncodingMacRoman)
}

// io_registry_entry_t

func (me *C.io_registry_entry_t) GetParent(plane string) (C.io_registry_entry_t, error) {
	cPlane := C.CString(plane)
	defer C.free(unsafe.Pointer(cPlane))
	var parent C.io_registry_entry_t
	err := C.IORegistryEntryGetParentEntry(*me, cPlane, &parent)
	if err != 0 {
		return 0, errors.New("No parent device available")
	}
	return parent, nil
}

func (me *C.io_registry_entry_t) GetClass() string {
	obj := (*C.io_object_t)(me)
	return obj.GetClass()
}

func (me *C.io_registry_entry_t) GetStringProperty(key string) (string, error) {
	k := cfStringCreateWithString(key)
	defer C.CFRelease(C.CFTypeRef(k))
	property := C.IORegistryEntryCreateCFProperty(*me, k, C.kCFAllocatorDefault, 0)
	if property == nil {
		return "", errors.New("Property not found: " + key)
	}
	defer C.CFRelease(property)

	if ptr := C.CFStringGetCStringPtr(property, 0); ptr != nil {
		return C.GoString(ptr), nil
	}
	// in certain circumstances CFStringGetCStringPtr may return NULL
	// and we must retrieve the string by copy
	buff := make([]C.char, 1024)
	if C.CFStringGetCString(property, &buff[0], 1024, 0) != C.true {
		return "", fmt.Errorf("Property '%s' can't be converted", key)
	}
	return C.GoString(&buff[0]), nil
}

func (me *C.io_registry_entry_t) GetIntProperty(key string, intType C.CFNumberType) (int, error) {
	k := cfStringCreateWithString(key)
	defer C.CFRelease(C.CFTypeRef(k))
	property := C.IORegistryEntryCreateCFProperty(*me, k, C.kCFAllocatorDefault, 0)
	if property == nil {
		return 0, errors.New("Property not found: " + key)
	}
	defer C.CFRelease(property)
	var res int
	if C.CFNumberGetValue(property, intType, unsafe.Pointer(&res)) != C.true {
		return res, fmt.Errorf("Property '%s' can't be converted or has been truncated", key)
	}
	return res, nil
}

// io_iterator_t

// IsValid checks if an iterator is still valid.
// Some iterators will be made invalid if changes are made to the
// structure they are iterating over. This function checks the iterator
// is still valid and should be called when Next returns zero.
// An invalid iterator can be Reset and the iteration restarted.
func (me *C.io_iterator_t) IsValid() bool {
	return C.IOIteratorIsValid(*me) == C.true
}

func (me *C.io_iterator_t) Reset() {
	C.IOIteratorReset(*me)
}

func (me *C.io_iterator_t) Next() (C.io_object_t, bool) {
	res := C.IOIteratorNext(*me)
	return res, res != 0
}

func (me *C.io_iterator_t) Release() {
	C.IOObjectRelease(C.io_object_t(*me))
}

// io_object_t

func (me *C.io_object_t) Release() {
	C.IOObjectRelease(*me)
}

func (me *C.io_object_t) GetClass() string {
	class := make([]C.char, 1024)
	C.IOObjectGetClass(*me, &class[0])
	return C.GoString(&class[0])
}
<|endoftext|>


<commit_before>// Copyright 2016 Google Inc. All Rights Reserved.
// This file is available under the Apache license.

package vm

import (
	"regexp/syntax"
	"testing"

	"github.com/go-test/deep"
)

var typeUnificationTests = []struct {
	a, b     Type
	expected Type
}{
	// The unification of None with None is still None.
	{
		None, None,
		None,
	},
	// Any type should unify to None with None.  You might call it the zero function.
	{
		None, Float,
		None,
	},
	{
		None, Int,
		None,
	},
	{
		Int, None,
		None,
	},
	{
		String, None,
		None,
	},
	// The unification of a type T with itself is T.
	{
		String, String,
		String,
	},
	{
		Int, Int,
		Int,
	},
	{
		Float, Float,
		Float,
	},
	{
		&TypeVariable{Id: 0}, &TypeVariable{Id: 0},
		&TypeVariable{Id: 0},
	},
	// The unification of any type operator with a type variable is the type operator
	{
		&TypeVariable{}, None,
		None,
	},
	{
		&TypeVariable{}, Float,
		Float,
	},
	{
		&TypeVariable{}, Int,
		Int,
	},
	{
		&TypeVariable{}, String,
		String,
	},
	{
		None, &TypeVariable{},
		None,
	},
	{
		Float, &TypeVariable{},
		Float,
	},
	{
		Int, &TypeVariable{},
		Int,
	},
	{
		String, &TypeVariable{},
		String,
	},
	// The lub of Int and Float is Float.
	{
		Int, Float,
		Float,
	},
	{
		Float, Int,
		Float,
	},
	// The lub of Int and String is String.
	{
		Int, String,
		String,
	},
	{
		String, Int,
		String,
	},
	// The lub of Float and String is String.
	{
		Float, String,
		String,
	},
	{
		String, Float,
		String,
	},
}

func TestTypeUnification(t *testing.T) {
	for i, tc := range typeUnificationTests {
		result := Unify(tc.a, tc.b)
		if diff := deep.Equal(result, tc.expected); len(diff) > 0 {
			t.Errorf("Result type not expected for %d: inputs %+v and %+v:\n%s", i, tc.a, tc.b, diff)
		}
	}
}

var groupOnlyMatchesTests = []struct {
	pattern  string
	check    string
	expected bool
}{
	{`\d+`,
		"0123456789",
		true,
	},
	{`[0123456789]`,
		"0123456789",
		true,
	},
	{`(0|1|2|3|4|5|6|7|8|9)`,
		"0123456789",
		true,
	},
	{`(\+|-)?\d+(\.\d+)?`,
		"0123456789",
		false,
	},
	{`(\d+\.\d+)`,
		"0123456789.eE+-",
		true,
	},
	{`(\+|-)?\d+(\.\d+)?`,
		"0123456789.eE+-",
		true,
	},
}

func TestGroupOnlyMatches(t *testing.T) {
	for _, tc := range groupOnlyMatchesTests {
		r, err := syntax.Parse(tc.pattern, syntax.Perl)
		if err != nil {
			t.Fatalf("syntax.Parse failed: %s", err)
		}
		result := groupOnlyMatches(r, tc.check)
		if result != tc.expected {
			t.Errorf("Pattern %q didn't only match check %q: expected %+v, received %+v", tc.pattern, tc.check, tc.expected, result)
		}
	}
}
<commit_msg>Add another test for floating point inference.<commit_after>// Copyright 2016 Google Inc. All Rights Reserved.
// This file is available under the Apache license.

package vm

import (
	"regexp/syntax"
	"testing"

	"github.com/go-test/deep"
)

var typeUnificationTests = []struct {
	a, b     Type
	expected Type
}{
	// The unification of None with None is still None.
	{
		None, None,
		None,
	},
	// Any type should unify to None with None.  You might call it the zero function.
	{
		None, Float,
		None,
	},
	{
		None, Int,
		None,
	},
	{
		Int, None,
		None,
	},
	{
		String, None,
		None,
	},
	// The unification of a type T with itself is T.
	{
		String, String,
		String,
	},
	{
		Int, Int,
		Int,
	},
	{
		Float, Float,
		Float,
	},
	{
		&TypeVariable{Id: 0}, &TypeVariable{Id: 0},
		&TypeVariable{Id: 0},
	},
	// The unification of any type operator with a type variable is the type operator
	{
		&TypeVariable{}, None,
		None,
	},
	{
		&TypeVariable{}, Float,
		Float,
	},
	{
		&TypeVariable{}, Int,
		Int,
	},
	{
		&TypeVariable{}, String,
		String,
	},
	{
		None, &TypeVariable{},
		None,
	},
	{
		Float, &TypeVariable{},
		Float,
	},
	{
		Int, &TypeVariable{},
		Int,
	},
	{
		String, &TypeVariable{},
		String,
	},
	// The lub of Int and Float is Float.
	{
		Int, Float,
		Float,
	},
	{
		Float, Int,
		Float,
	},
	// The lub of Int and String is String.
	{
		Int, String,
		String,
	},
	{
		String, Int,
		String,
	},
	// The lub of Float and String is String.
	{
		Float, String,
		String,
	},
	{
		String, Float,
		String,
	},
}

func TestTypeUnification(t *testing.T) {
	for i, tc := range typeUnificationTests {
		result := Unify(tc.a, tc.b)
		if diff := deep.Equal(result, tc.expected); len(diff) > 0 {
			t.Errorf("Result type not expected for %d: inputs %+v and %+v:\n%s", i, tc.a, tc.b, diff)
		}
	}
}

var groupOnlyMatchesTests = []struct {
	pattern  string
	check    string
	expected bool
}{
	{`\d+`,
		"0123456789",
		true,
	},
	{`[0123456789]`,
		"0123456789",
		true,
	},
	{`(0|1|2|3|4|5|6|7|8|9)`,
		"0123456789",
		true,
	},
	{`(\+|-)?\d+(\.\d+)?`,
		"0123456789",
		false,
	},
	{`(\d+\.\d+)`,
		"0123456789.eE+-",
		true,
	},
	{`(\+|-)?\d+(\.\d+)?`,
		"0123456789.eE+-",
		true,
	},
	{`(?P<offset>-?\d+\.\d+)`,
		"0123456789.eE+-",
		true,
	},
}

func TestGroupOnlyMatches(t *testing.T) {
	for _, tc := range groupOnlyMatchesTests {
		r, err := syntax.Parse(tc.pattern, syntax.Perl)
		if err != nil {
			t.Fatalf("syntax.Parse failed: %s", err)
		}
		result := groupOnlyMatches(r, tc.check)
		if result != tc.expected {
			t.Errorf("Pattern %q didn't only match check %q: expected %+v, received %+v", tc.pattern, tc.check, tc.expected, result)
		}
	}
}
<|endoftext|>


<commit_before>// Copyright 2014 The Serviced Authors.
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package volume

import (
	"bytes"
	"errors"
	"fmt"
	"io"
	"path"
	"path/filepath"

	"github.com/zenoss/glog"
)

// DriverInit represents a function that can initialize a driver.
type DriverInit func(root string, args []string) (Driver, error)

// DriverType represents a driver type.
type DriverType string

type Usage struct {
	Label string
	Type  string
	Value uint64
}

type Status struct { // see Docker - look at their status struct and borrow heavily.
	Driver     DriverType
	DriverData map[string]string
	UsageData  []Usage
}

type Statuses struct {
	StatusMap map[string]Status
}

const (
	DriverTypeBtrFS        DriverType = "btrfs"
	DriverTypeRsync        DriverType = "rsync"
	DriverTypeDeviceMapper DriverType = "devicemapper"
	DriverTypeNFS          DriverType = "nfs"
)

var (
	drivers       map[DriverType]DriverInit
	driversByRoot map[string]Driver

	ErrInvalidDriverInit       = errors.New("invalid driver initializer")
	ErrDriverNotInit           = errors.New("driver not initialized")
	ErrDriverAlreadyInit       = errors.New("different driver already initialized")
	ErrDriverExists            = errors.New("driver exists")
	ErrDriverNotSupported      = errors.New("driver not supported")
	ErrRemovingVolume          = errors.New("could not remove volume")
	ErrSnapshotExists          = errors.New("snapshot exists")
	ErrSnapshotDoesNotExist    = errors.New("snapshot does not exist")
	ErrRemovingSnapshot        = errors.New("could not remove snapshot")
	ErrBadDriverShutdown       = errors.New("unable to shutdown driver")
	ErrVolumeExists            = errors.New("volume exists")
	ErrPathIsDriver            = errors.New("path is initialized as a driver")
	ErrPathIsNotAbs            = errors.New("path is not absolute")
	ErrBadMount                = errors.New("bad mount path")
	ErrInsufficientPermissions = errors.New("insufficient permissions to run command")
)

func init() {
	drivers = make(map[DriverType]DriverInit)
	driversByRoot = make(map[string]Driver)
}

// Driver is the basic interface to the filesystem. It is able to create,
// manage and destroy volumes. It is initialized with and operates beneath
// a given directory.
type Driver interface {
	// Root returns the filesystem root this driver acts on
	Root() string
	// DriverType returns the string describing the driver
	DriverType() DriverType
	// Create creates a volume with the given name and returns it. The volume
	// must not exist already.
	Create(volumeName string) (Volume, error)
	// Remove removes an existing device. If the device doesn't exist, the
	// removal is a no-op
	Remove(volumeName string) error
	// Get returns the volume with the given name. The volume must exist.
	Get(volumeName string) (Volume, error)
	// Release releases any runtime resources associated with a volume (e.g.,
	// unmounts a device)
	Release(volumeName string) error
	// List returns the names of all volumes managed by this driver
	List() []string
	// Exists returns whether or not a volume managed by this driver exists
	// with the given name
	Exists(volumeName string) bool
	// Cleanup releases any runtime resources held by the driver itself.
	Cleanup() error
	// Status gets the status of the volume
	Status() (*Status, error)
}

// Volume maps, in the end, to a directory on the filesystem available to the
// application. It can be snapshotted and rolled back to snapshots. It can be
// exported to a file and restored from a file.
type Volume interface {
	// Name returns the name of this volume
	Name() string
	// Path returns the filesystem path to this volume
	Path() string
	// Driver returns the driver managing this volume
	Driver() Driver
	// Snapshot snapshots the current state of this volume and stores it
	// using the name <label>
	Snapshot(label string) (err error)
	// WriteMetadata returns a handle to write metadata to a snapshot
	WriteMetadata(label, name string) (io.WriteCloser, error)
	// ReadMetadata returns a handle to read metadata from a snapshot
	ReadMetadata(label, name string) (io.ReadCloser, error)
	// Snapshots lists all snapshots of this volume
	Snapshots() ([]string, error)
	// RemoveSnapshot removes the snapshot with name <label>
	RemoveSnapshot(label string) error
	// Rollback replaces the current state of the volume with that snapshotted
	// as <label>
	Rollback(label string) error
	// Export exports the snapshot stored as <label> to <filename>
	Export(label, parent string, writer io.Writer) error
	// Import imports the exported snapshot at <filename> as <label>
	Import(label string, reader io.Reader) error
	// Tenant returns the base tenant of this volume
	Tenant() string
}

// Register registers a driver initializer under <name> so it can be looked up
func Register(name DriverType, driverInit DriverInit) error {
	if driverInit == nil {
		return ErrInvalidDriverInit
	}
	if _, dup := drivers[name]; dup {
		return ErrDriverExists
	}
	drivers[name] = driverInit
	return nil
}

// Registered returns a boolean indicating whether driver <name> has been registered.
func Registered(name DriverType) bool {
	_, ok := drivers[name]
	return ok
}

// Unregister the driver init func <name>. If it doesn't exist, it's a no-op.
func Unregister(name DriverType) {
	delete(drivers, name)
	// Also delete any existing drivers using this name
	for root, drv := range driversByRoot {
		if drv.DriverType() == name {
			delete(driversByRoot, root)
		}
	}
}

// InitDriver sets up a driver <name> and initializes it to <root>.
func InitDriver(name DriverType, root string, args []string) error {
	// Make sure it is a driver that exists
	if init, exists := drivers[name]; exists {
		// Clean the path
		root = filepath.Clean(root)
		// If the driver already exists, return
		if _, exists := driversByRoot[root]; exists {
			return nil
		}
		// Can only add absolute paths
		if !path.IsAbs(root) {
			return ErrPathIsNotAbs
		}
		if name != DriverTypeNFS {
			// Check for an existing driver initialization that doesn't match
			if t, err := DetectDriverType(root); err != nil {
				if err != ErrDriverNotInit {
					return err
				}
			} else if t != name {
				glog.Errorf("Unable to initialize %s driver. Path %s has an existing %s volume driver.", name, root, t)
				return ErrDriverAlreadyInit
			}
		}
		// Create the driver instance
		driver, err := init(root, args)
		if err != nil {
			return err
		}
		driversByRoot[root] = driver
		return nil
	}
	return ErrDriverNotSupported
}

// GetDriver returns the driver from path <root>.
func GetDriver(root string) (Driver, error) {
	driver, ok := driversByRoot[filepath.Clean(root)]
	if !ok {
		return nil, ErrDriverNotInit
	}
	return driver, nil
}

// SplitPath splits a path by its driver and respective volume.  Returns
// error if the driver is not initialized.
func SplitPath(volumePath string) (string, string, error) {
	// Validate the path
	rootDir := filepath.Clean(volumePath)
	if !filepath.IsAbs(rootDir) {
		// must be absolute
		return "", "", ErrPathIsNotAbs
	}
	if _, ok := driversByRoot[rootDir]; ok {
		return volumePath, "", nil
	}
	for {
		rootDir = filepath.Dir(rootDir)
		if _, ok := driversByRoot[rootDir]; !ok {
			// continue if the path is not '/'
			if rootDir == "/" {
				return "", "", ErrDriverNotInit
			}
		} else {
			// get the name of the volume
			if volumeName, err := filepath.Rel(rootDir, volumePath); err != nil {
				glog.Errorf("Unexpected error while looking up relpath of %s from %s: %s", volumePath, rootDir, err)
				return "", "", err
			} else {
				return rootDir, volumeName, nil
			}
		}
	}
}

// FindMount mounts a path based on the relative location of the nearest driver.
func FindMount(volumePath string) (Volume, error) {
	rootDir, volumeName, err := SplitPath(volumePath)
	if err != nil {
		return nil, err
	} else if rootDir == volumePath {
		return nil, ErrPathIsDriver
	}
	return Mount(volumeName, rootDir)
}

// Mount loads, mounting if necessary, a volume under a path using a specific
// driver path at <root>.
func Mount(volumeName, rootDir string) (volume Volume, err error) {
	// Make sure the volume can be created from root
	if rDir, vName, err := SplitPath(filepath.Join(rootDir, volumeName)); err != nil {
		return nil, err
	} else if rDir != rootDir {
		glog.Errorf("Cannot mount volume at %s; found root at %s", rootDir, rDir)
		return nil, ErrBadMount
	} else if vName == "" {
		glog.Errorf("Volume '%s' at %s is a driver", volumeName, rootDir)
		return nil, ErrPathIsDriver
	}
	glog.V(1).Infof("Mounting volume %s via %s", volumeName, rootDir)
	driver, err := GetDriver(rootDir)
	if err != nil {
		glog.Errorf("Could not get driver from root %s: %s", rootDir, err)
		return nil, err
	}
	glog.V(2).Infof("Got %s driver for %s", driver.DriverType(), driver.Root())
	if driver.Exists(volumeName) {
		glog.V(2).Infof("Volume %s exists; remounting", volumeName)
		volume, err = driver.Get(volumeName)
	} else {
		glog.V(2).Infof("Volume %s does not exist; creating", volumeName)
		volume, err = driver.Create(volumeName)
	}
	if err != nil {
		glog.Errorf("Error mounting volume: %s", err)
		return nil, err
	}
	return volume, nil
}

// ShutdownDriver shuts down an existing driver and removes it from our internal map.
func ShutdownDriver(rootDir string) error {
	driver, ok := driversByRoot[rootDir]
	if !ok {
		glog.Errorf("Tried to shut down uninitialized driver: %s", rootDir)
		return ErrDriverNotInit
	}
	glog.V(2).Infof("Shutting down %s driver for %s", driver.DriverType(), driver.Root())
	if err := driver.Cleanup(); err != nil {
		glog.Errorf("Unable to clean up %s driver for %s: %s", driver.DriverType(), driver.Root(), err)
		return err
	}
	delete(driversByRoot, rootDir)
	return nil
}

// ShutdownAll shuts down all drivers that have been initialized
func ShutdownAll() error {
	errs := []error{}
	for root, _ := range driversByRoot {
		if err := ShutdownDriver(root); err != nil {
			errs = append(errs, err)
		}
	}
	if len(errs) > 0 {
		return ErrBadDriverShutdown
	}
	return nil
}

// GetStatus retrieves the status for the volumeNames passed in. If volumeNames is empty, it getst all statuses.
func GetStatus() *Statuses {
	result := &Statuses{}
	result.StatusMap = make(map[string]Status)
	driverMap := getDrivers()
	for path, driver := range *driverMap {
		status, err := driver.Status()
		if err != nil {
			glog.Warningf("Error getting driver status for path %s: %v", path, err)
		}
		if status != nil {
			result.StatusMap[path] = *status
		} else {
			glog.Warningf("nil status returned for path %s", path)
		}
	}
	return result
}

// getDrivers retrieves the driver for each volumeName passed in.
// if volumeNames is empty, the function returns all drivers, with their roots.
func getDrivers() *map[string]Driver {
	glog.Infof("getDrivers(): returning driversByRoot(%q)", driversByRoot)
	return &driversByRoot
}

func StringToDriverType(name string) (DriverType, error) {
	switch name {
	case "btrfs":
		return DriverTypeBtrFS, nil
	case "rsync":
		return DriverTypeRsync, nil
	case "devicemapper":
		return DriverTypeDeviceMapper, nil
	}
	return "", ErrDriverNotSupported
}

func (s Status) String() string {
	var buffer bytes.Buffer
	buffer.WriteString(fmt.Sprintf("Driver:                 %s\n", s.Driver))
	for key, value := range s.DriverData {
		buffer.WriteString(fmt.Sprintf("%-24s%s\n", fmt.Sprintf("%s:", key), value))
	}
	buffer.WriteString(fmt.Sprintf("Usage Data:\n"))
	for _, usage := range s.UsageData {
		buffer.WriteString(fmt.Sprintf("\t%s %s: %d\n", usage.Label, usage.Type, usage.Value))
	}
	return buffer.String()
}
<commit_msg>CC-1185: Make sure directory that we're going to init to exists<commit_after>// Copyright 2014 The Serviced Authors.
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package volume

import (
	"bytes"
	"errors"
	"fmt"
	"io"
	"os"
	"path"
	"path/filepath"

	"github.com/zenoss/glog"
)

// DriverInit represents a function that can initialize a driver.
type DriverInit func(root string, args []string) (Driver, error)

// DriverType represents a driver type.
type DriverType string

type Usage struct {
	Label string
	Type  string
	Value uint64
}

type Status struct { // see Docker - look at their status struct and borrow heavily.
	Driver     DriverType
	DriverData map[string]string
	UsageData  []Usage
}

type Statuses struct {
	StatusMap map[string]Status
}

const (
	DriverTypeBtrFS        DriverType = "btrfs"
	DriverTypeRsync        DriverType = "rsync"
	DriverTypeDeviceMapper DriverType = "devicemapper"
	DriverTypeNFS          DriverType = "nfs"
)

var (
	drivers       map[DriverType]DriverInit
	driversByRoot map[string]Driver

	ErrInvalidDriverInit       = errors.New("invalid driver initializer")
	ErrDriverNotInit           = errors.New("driver not initialized")
	ErrDriverAlreadyInit       = errors.New("different driver already initialized")
	ErrDriverExists            = errors.New("driver exists")
	ErrDriverNotSupported      = errors.New("driver not supported")
	ErrRemovingVolume          = errors.New("could not remove volume")
	ErrSnapshotExists          = errors.New("snapshot exists")
	ErrSnapshotDoesNotExist    = errors.New("snapshot does not exist")
	ErrRemovingSnapshot        = errors.New("could not remove snapshot")
	ErrBadDriverShutdown       = errors.New("unable to shutdown driver")
	ErrVolumeExists            = errors.New("volume exists")
	ErrPathIsDriver            = errors.New("path is initialized as a driver")
	ErrPathIsNotAbs            = errors.New("path is not absolute")
	ErrBadMount                = errors.New("bad mount path")
	ErrInsufficientPermissions = errors.New("insufficient permissions to run command")
)

func init() {
	drivers = make(map[DriverType]DriverInit)
	driversByRoot = make(map[string]Driver)
}

// Driver is the basic interface to the filesystem. It is able to create,
// manage and destroy volumes. It is initialized with and operates beneath
// a given directory.
type Driver interface {
	// Root returns the filesystem root this driver acts on
	Root() string
	// DriverType returns the string describing the driver
	DriverType() DriverType
	// Create creates a volume with the given name and returns it. The volume
	// must not exist already.
	Create(volumeName string) (Volume, error)
	// Remove removes an existing device. If the device doesn't exist, the
	// removal is a no-op
	Remove(volumeName string) error
	// Get returns the volume with the given name. The volume must exist.
	Get(volumeName string) (Volume, error)
	// Release releases any runtime resources associated with a volume (e.g.,
	// unmounts a device)
	Release(volumeName string) error
	// List returns the names of all volumes managed by this driver
	List() []string
	// Exists returns whether or not a volume managed by this driver exists
	// with the given name
	Exists(volumeName string) bool
	// Cleanup releases any runtime resources held by the driver itself.
	Cleanup() error
	// Status gets the status of the volume
	Status() (*Status, error)
}

// Volume maps, in the end, to a directory on the filesystem available to the
// application. It can be snapshotted and rolled back to snapshots. It can be
// exported to a file and restored from a file.
type Volume interface {
	// Name returns the name of this volume
	Name() string
	// Path returns the filesystem path to this volume
	Path() string
	// Driver returns the driver managing this volume
	Driver() Driver
	// Snapshot snapshots the current state of this volume and stores it
	// using the name <label>
	Snapshot(label string) (err error)
	// WriteMetadata returns a handle to write metadata to a snapshot
	WriteMetadata(label, name string) (io.WriteCloser, error)
	// ReadMetadata returns a handle to read metadata from a snapshot
	ReadMetadata(label, name string) (io.ReadCloser, error)
	// Snapshots lists all snapshots of this volume
	Snapshots() ([]string, error)
	// RemoveSnapshot removes the snapshot with name <label>
	RemoveSnapshot(label string) error
	// Rollback replaces the current state of the volume with that snapshotted
	// as <label>
	Rollback(label string) error
	// Export exports the snapshot stored as <label> to <filename>
	Export(label, parent string, writer io.Writer) error
	// Import imports the exported snapshot at <filename> as <label>
	Import(label string, reader io.Reader) error
	// Tenant returns the base tenant of this volume
	Tenant() string
}

// Register registers a driver initializer under <name> so it can be looked up
func Register(name DriverType, driverInit DriverInit) error {
	if driverInit == nil {
		return ErrInvalidDriverInit
	}
	if _, dup := drivers[name]; dup {
		return ErrDriverExists
	}
	drivers[name] = driverInit
	return nil
}

// Registered returns a boolean indicating whether driver <name> has been registered.
func Registered(name DriverType) bool {
	_, ok := drivers[name]
	return ok
}

// Unregister the driver init func <name>. If it doesn't exist, it's a no-op.
func Unregister(name DriverType) {
	delete(drivers, name)
	// Also delete any existing drivers using this name
	for root, drv := range driversByRoot {
		if drv.DriverType() == name {
			delete(driversByRoot, root)
		}
	}
}

// InitDriver sets up a driver <name> and initializes it to <root>.
func InitDriver(name DriverType, root string, args []string) error {
	// Make sure it is a driver that exists
	if init, exists := drivers[name]; exists {
		// Clean the path
		root = filepath.Clean(root)
		// If the driver already exists, return
		if _, exists := driversByRoot[root]; exists {
			return nil
		}
		// Can only add absolute paths
		if !path.IsAbs(root) {
			return ErrPathIsNotAbs
		}
		if name != DriverTypeNFS {
			// Check for an existing driver initialization that doesn't match
			if t, err := DetectDriverType(root); err != nil {
				if err != ErrDriverNotInit {
					return err
				}
			} else if t != name {
				glog.Errorf("Unable to initialize %s driver. Path %s has an existing %s volume driver.", name, root, t)
				return ErrDriverAlreadyInit
			}
		}
		// Create the directory
		if err := os.MkdirAll(root, 0755); err != nil {
			return err
		}
		// Create the driver instance
		driver, err := init(root, args)
		if err != nil {
			return err
		}
		driversByRoot[root] = driver
		return nil
	}
	return ErrDriverNotSupported
}

// GetDriver returns the driver from path <root>.
func GetDriver(root string) (Driver, error) {
	driver, ok := driversByRoot[filepath.Clean(root)]
	if !ok {
		return nil, ErrDriverNotInit
	}
	return driver, nil
}

// SplitPath splits a path by its driver and respective volume.  Returns
// error if the driver is not initialized.
func SplitPath(volumePath string) (string, string, error) {
	// Validate the path
	rootDir := filepath.Clean(volumePath)
	if !filepath.IsAbs(rootDir) {
		// must be absolute
		return "", "", ErrPathIsNotAbs
	}
	if _, ok := driversByRoot[rootDir]; ok {
		return volumePath, "", nil
	}
	for {
		rootDir = filepath.Dir(rootDir)
		if _, ok := driversByRoot[rootDir]; !ok {
			// continue if the path is not '/'
			if rootDir == "/" {
				return "", "", ErrDriverNotInit
			}
		} else {
			// get the name of the volume
			if volumeName, err := filepath.Rel(rootDir, volumePath); err != nil {
				glog.Errorf("Unexpected error while looking up relpath of %s from %s: %s", volumePath, rootDir, err)
				return "", "", err
			} else {
				return rootDir, volumeName, nil
			}
		}
	}
}

// FindMount mounts a path based on the relative location of the nearest driver.
func FindMount(volumePath string) (Volume, error) {
	rootDir, volumeName, err := SplitPath(volumePath)
	if err != nil {
		return nil, err
	} else if rootDir == volumePath {
		return nil, ErrPathIsDriver
	}
	return Mount(volumeName, rootDir)
}

// Mount loads, mounting if necessary, a volume under a path using a specific
// driver path at <root>.
func Mount(volumeName, rootDir string) (volume Volume, err error) {
	// Make sure the volume can be created from root
	if rDir, vName, err := SplitPath(filepath.Join(rootDir, volumeName)); err != nil {
		return nil, err
	} else if rDir != rootDir {
		glog.Errorf("Cannot mount volume at %s; found root at %s", rootDir, rDir)
		return nil, ErrBadMount
	} else if vName == "" {
		glog.Errorf("Volume '%s' at %s is a driver", volumeName, rootDir)
		return nil, ErrPathIsDriver
	}
	glog.V(1).Infof("Mounting volume %s via %s", volumeName, rootDir)
	driver, err := GetDriver(rootDir)
	if err != nil {
		glog.Errorf("Could not get driver from root %s: %s", rootDir, err)
		return nil, err
	}
	glog.V(2).Infof("Got %s driver for %s", driver.DriverType(), driver.Root())
	if driver.Exists(volumeName) {
		glog.V(2).Infof("Volume %s exists; remounting", volumeName)
		volume, err = driver.Get(volumeName)
	} else {
		glog.V(2).Infof("Volume %s does not exist; creating", volumeName)
		volume, err = driver.Create(volumeName)
	}
	if err != nil {
		glog.Errorf("Error mounting volume: %s", err)
		return nil, err
	}
	return volume, nil
}

// ShutdownDriver shuts down an existing driver and removes it from our internal map.
func ShutdownDriver(rootDir string) error {
	driver, ok := driversByRoot[rootDir]
	if !ok {
		glog.Errorf("Tried to shut down uninitialized driver: %s", rootDir)
		return ErrDriverNotInit
	}
	glog.V(2).Infof("Shutting down %s driver for %s", driver.DriverType(), driver.Root())
	if err := driver.Cleanup(); err != nil {
		glog.Errorf("Unable to clean up %s driver for %s: %s", driver.DriverType(), driver.Root(), err)
		return err
	}
	delete(driversByRoot, rootDir)
	return nil
}

// ShutdownAll shuts down all drivers that have been initialized
func ShutdownAll() error {
	errs := []error{}
	for root, _ := range driversByRoot {
		if err := ShutdownDriver(root); err != nil {
			errs = append(errs, err)
		}
	}
	if len(errs) > 0 {
		return ErrBadDriverShutdown
	}
	return nil
}

// GetStatus retrieves the status for the volumeNames passed in. If volumeNames is empty, it getst all statuses.
func GetStatus() *Statuses {
	result := &Statuses{}
	result.StatusMap = make(map[string]Status)
	driverMap := getDrivers()
	for path, driver := range *driverMap {
		status, err := driver.Status()
		if err != nil {
			glog.Warningf("Error getting driver status for path %s: %v", path, err)
		}
		if status != nil {
			result.StatusMap[path] = *status
		} else {
			glog.Warningf("nil status returned for path %s", path)
		}
	}
	return result
}

// getDrivers retrieves the driver for each volumeName passed in.
// if volumeNames is empty, the function returns all drivers, with their roots.
func getDrivers() *map[string]Driver {
	glog.Infof("getDrivers(): returning driversByRoot(%q)", driversByRoot)
	return &driversByRoot
}

func StringToDriverType(name string) (DriverType, error) {
	switch name {
	case "btrfs":
		return DriverTypeBtrFS, nil
	case "rsync":
		return DriverTypeRsync, nil
	case "devicemapper":
		return DriverTypeDeviceMapper, nil
	}
	return "", ErrDriverNotSupported
}

func (s Status) String() string {
	var buffer bytes.Buffer
	buffer.WriteString(fmt.Sprintf("Driver:                 %s\n", s.Driver))
	for key, value := range s.DriverData {
		buffer.WriteString(fmt.Sprintf("%-24s%s\n", fmt.Sprintf("%s:", key), value))
	}
	buffer.WriteString(fmt.Sprintf("Usage Data:\n"))
	for _, usage := range s.UsageData {
		buffer.WriteString(fmt.Sprintf("\t%s %s: %d\n", usage.Label, usage.Type, usage.Value))
	}
	return buffer.String()
}
<|endoftext|>