taleintervenor dfaef9d393 syscontainer-tools: update license to Mulan PSL v2
reason: update license to Mulan PSL v2

Signed-off-by: taleintervenor <taleintervenor@aliyun.com>
2020-04-27 14:54:17 +08:00

226 lines
6.9 KiB
Go

// Copyright (c) Huawei Technologies Co., Ltd. 2018-2019. All rights reserved.
// syscontainer-tools is licensed under the Mulan PSL v2.
// You can use this software according to the terms and conditions of the Mulan PSL v2.
// You may obtain a copy of Mulan PSL v2 at:
// http://license.coscl.org.cn/MulanPSL2
// THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR
// PURPOSE.
// See the Mulan PSL v2 for more details.
// Description: device hook config
// Author: zhangwei
// Create: 2018-01-18
package config
import (
"encoding/json"
"fmt"
"io/ioutil"
"os"
"path/filepath"
"reflect"
"strings"
"sync"
"isula.org/syscontainer-tools/container"
"isula.org/syscontainer-tools/types"
)
const (
defaultConfigFile = "device_hook.json"
// IsuladToolsDir is syscontainer-tools run dir
IsuladToolsDir = "/run/syscontainer-tools"
)
// QosType defines the qos type by int
type QosType int
const (
// QosReadIOPS defines the read device iops type
QosReadIOPS QosType = iota
// QosWriteIOPS defines the write device iops type
QosWriteIOPS
// QosReadBps defines the read device bps type
QosReadBps
// QosWriteBps defines the write device bps type
QosWriteBps
// QosBlkioWeight defines the device blkio weight type
QosBlkioWeight
)
// ContainerConfig is the interface of container config handler
type ContainerConfig interface {
FindDeviceByMapping(dev *types.Device) *types.Device
FindSubPartition(dev *types.Device) []*types.Device
UpdateDevice(device *types.Device, isAddDevice bool) error
UpdateDeviceNode(device string, major, minor int64)
IsBindInConfig(bind *types.Bind) bool
UpdateBind(bind *types.Bind, isAddBind bool) (bool, error)
GetBinds() []string
GetBindInConfig(bind *types.Bind) (*HostMapping, error)
GetAllDevices() []*DeviceMapping
DeviceIndexInArray(device *types.Device) int
UpdateDeviceQos(qos *types.Qos, qType QosType) error
RemoveDeviceQos(device *types.Device, qType QosType) (bool, error)
FindInterfaceByName(config *types.InterfaceConf) *types.InterfaceConf
IsConflictInterface(nic *types.InterfaceConf) error
IsSameInterface(nic *types.InterfaceConf) bool
UpdateNetworkInterface(nic *types.InterfaceConf, isAdd bool) error
GetNics(filter *types.InterfaceConf) []*types.InterfaceConf
IsRouteExist(route *types.Route) bool
IsConflictRoute(route *types.Route) error
UpdateNetworkRoutes(route *types.Route, isAdd bool) error
GetRoutes(filter *types.Route) []*types.Route
Flush() error
CheckPathNum() error
CheckNicNum() error
}
// NewContainerConfig will create the container config handler by name
func NewContainerConfig(c *container.Container) (ContainerConfig, error) {
configfile := filepath.Join(c.ContainerPath(), defaultConfigFile)
hConfig, err := LoadContainerHookConfig(configfile)
if err != nil {
return nil, err
}
hConfig.configPath = configfile
return hConfig, nil
}
// DeviceMapping represents the device mapping between the host and the container.
type DeviceMapping struct {
Type string
Minor int64
Major int64
PathOnHost string
PathInContainer string
CgroupPermissions string
Parent string
}
type info struct {
count int
perm string
}
type bindsInfo struct {
pathInHost map[string]*info
pathInContainer map[string]int
l *sync.Mutex
}
func checkEuqal(old, new string) bool {
oldp := strings.Split(strings.Replace(old, " ", "", -1), ",")
newp := strings.Split(strings.Replace(new, " ", "", -1), ",")
return reflect.DeepEqual(oldp, newp)
}
// we do not allow mount more than on host paths to a single path in container
// if we mount a single host path to multi paths in contiainer, return true,nil
func (bi *bindsInfo) add(bindstr string) (bool, error) {
bi.l.Lock()
defer bi.l.Unlock()
hostPathExist := false
mp, err := parseMapping(bindstr)
if err != nil {
return hostPathExist, fmt.Errorf("Wrong bind format: %s,err %s", bindstr, err)
}
if _, exist := bi.pathInContainer[mp.PathInContainer]; exist == true {
return hostPathExist, fmt.Errorf("Mount more than one host paths to a single path in container")
}
bi.pathInContainer[mp.PathInContainer] = 1
if _, exist := bi.pathInHost[mp.PathOnHost]; exist == true {
if checkEuqal(mp.Permission, bi.pathInHost[mp.PathOnHost].perm) == false {
return hostPathExist, fmt.Errorf("Mount one host path with different permissions, old: %s, new: %s", bi.pathInHost[mp.PathOnHost].perm, mp.Permission)
}
bi.pathInHost[mp.PathOnHost].count++
hostPathExist = true
return hostPathExist, nil
}
bi.pathInHost[mp.PathOnHost] = &info{count: 1, perm: mp.Permission}
return hostPathExist, nil
}
func (bi *bindsInfo) remove(bindstr string) (bool, error) {
bi.l.Lock()
defer bi.l.Unlock()
mp, err := parseMapping(bindstr)
if err != nil {
return true, fmt.Errorf("Wrong bind format: %s,err %s", bindstr, err)
}
// always delete the item of container path
delete(bi.pathInContainer, mp.PathInContainer)
if _, exist := bi.pathInHost[mp.PathOnHost]; exist == true {
bi.pathInHost[mp.PathOnHost].count--
if bi.pathInHost[mp.PathOnHost].count <= 0 {
delete(bi.pathInHost, mp.PathOnHost)
return true, nil
}
return false, nil
}
return true, fmt.Errorf("%s not in memory datebase", mp.PathOnHost)
}
// ContainerHookConfig is the data config structure for device hook storage file.
type ContainerHookConfig struct {
Binds []string `json:"bindToAdd,omitempty"`
Devices []*DeviceMapping `json:"deviceToAdd,omitempty"`
ReadIOPS []*types.Qos `json:"readIops,omitempty"`
WriteIOPS []*types.Qos `json:"writeIops,omitempty"`
ReadBps []*types.Qos `json:"readBps,omitempty"`
WriteBps []*types.Qos `json:"writeBps,omitempty"`
BlkioWeight []*types.Qos `json:"blkioWeight,omitempty"`
NetworkInterfaces []*types.InterfaceConf `json:"networkInterfaces,omitempty"`
NetworkRoutes []*types.Route `json:"networkRoute,omitempty"`
configPath string
dirty bool
bi *bindsInfo
}
// LoadContainerHookConfig will parse and unmarshal ContainerHookConfig
func LoadContainerHookConfig(path string) (*ContainerHookConfig, error) {
// if config file do not exist, just return empty DevieHookConfig.
bi := bindsInfo{
pathInHost: make(map[string]*info),
pathInContainer: make(map[string]int),
l: &sync.Mutex{},
}
if _, err := os.Stat(path); err != nil {
return &ContainerHookConfig{bi: &bi}, nil
}
bytes, err := ioutil.ReadFile(path)
if err != nil {
return nil, err
}
config := &ContainerHookConfig{}
if err := json.Unmarshal(bytes, &config); err != nil {
return nil, err
}
config.bi = &bi
for _, bindstr := range config.Binds {
if _, err := config.bi.add(bindstr); err != nil {
return nil, err
}
}
config.configPath = path
return config, nil
}