From 090984f9d29960da84efe8ff1e017cea145f1794 Mon Sep 17 00:00:00 2001 From: Qiu Jian Date: Tue, 19 Mar 2019 12:28:21 +0800 Subject: [PATCH 01/10] fix: 1. abstract baseagent, 2. extend baseagent to baremetalagent --- pkg/baremetal/agent.go | 339 +++++-------------------------- pkg/baremetal/manager.go | 2 +- pkg/baremetal/service/service.go | 1 + pkg/cloudcommon/agent/agent.go | 334 ++++++++++++++++++++++++++++++ 4 files changed, 388 insertions(+), 288 deletions(-) create mode 100644 pkg/cloudcommon/agent/agent.go diff --git a/pkg/baremetal/agent.go b/pkg/baremetal/agent.go index 786a791af3..5caad1609a 100644 --- a/pkg/baremetal/agent.go +++ b/pkg/baremetal/agent.go @@ -4,96 +4,67 @@ import ( "context" "fmt" "net" - "time" - "yunion.io/x/jsonutils" "yunion.io/x/log" o "yunion.io/x/onecloud/pkg/baremetal/options" "yunion.io/x/onecloud/pkg/baremetal/pxe" + "yunion.io/x/onecloud/pkg/cloudcommon/agent" "yunion.io/x/onecloud/pkg/mcclient" "yunion.io/x/onecloud/pkg/mcclient/auth" - "yunion.io/x/onecloud/pkg/mcclient/modules" "yunion.io/x/onecloud/pkg/util/procutils" ) +const ( + AGENT_TYPE_BAREMETAL = "baremetal" +) + var ( baremetalAgent *SBaremetalAgent ) -type SZone struct { - Name string `json:"name"` - Id string `json:"id"` -} - type SBaremetalAgent struct { - PXEServer *pxe.Server - ListenInterface *net.Interface - AgentId string - AgentName string - Zone *SZone - Manager *SBaremetalManager + agent.SBaseAgent + + PXEServer *pxe.Server + Manager *SBaremetalManager } func newBaremetalAgent() (*SBaremetalAgent, error) { - iface, err := net.InterfaceByName(o.Options.ListenInterface) + agent := &SBaremetalAgent{} + err := agent.Init(agent, o.Options.ListenInterface) if err != nil { return nil, err } - ips, err := getIfaceIPs(iface) - if err != nil { - return nil, err - } - if len(ips) == 0 { - return nil, fmt.Errorf("Interface %s ip address not found", o.Options.ListenInterface) - } - log.Debugf("Interface %s ip address: %v", iface.Name, ips) - - agent := &SBaremetalAgent{ - ListenInterface: iface, - } return agent, nil } -func GetAdminSession() *mcclient.ClientSession { +func (agent *SBaremetalAgent) GetAgentType() string { + return AGENT_TYPE_BAREMETAL +} + +func (agent *SBaremetalAgent) GetPort() int { + return o.Options.Port +} + +func (agent *SBaremetalAgent) GetEnableSsl() bool { + return o.Options.EnableSsl +} + +func (agent *SBaremetalAgent) GetZoneName() string { + return o.Options.Zone +} + +func (agent *SBaremetalAgent) GetAdminSession() *mcclient.ClientSession { return auth.GetAdminSession(context.TODO(), o.Options.Region, "v2") } -func (agent *SBaremetalAgent) GetListenIPs() ([]net.IP, error) { - ips, err := getIfaceIPs(agent.ListenInterface) - if err != nil { - return nil, err - } - if len(ips) == 0 { - return nil, fmt.Errorf("Interface %s ip address not found", agent.ListenInterface.Name) - } - return ips, nil -} - func (agent *SBaremetalAgent) GetListenIP() (net.IP, error) { - ips, err := agent.GetListenIPs() - if err != nil { - return nil, err - } - if o.Options.ListenAddress == "" { - return ips[0], nil - } - if o.Options.ListenAddress == "0.0.0.0" { - return net.ParseIP(o.Options.ListenAddress), nil - } - for _, ip := range ips { - if ip.String() == o.Options.ListenAddress { - return ip, nil - } - } - return nil, fmt.Errorf("Not found ListenAddress %s on %s", o.Options.ListenAddress, o.Options.ListenInterface) + return agent.FindListenIP(o.Options.ListenAddress) } func (agent *SBaremetalAgent) GetDHCPServerListenIP() (net.IP, error) { - ips, err := agent.GetListenIPs() - if err != nil { - return nil, err - } + ips := agent.GetListenIPs() // baremetal dhcp server can't bind address 0.0.0.0:67, conflict with host agent // but can bind specific ip address, because socket set reuseaddr option @@ -109,19 +80,7 @@ func (agent *SBaremetalAgent) GetDHCPServerListenIP() (net.IP, error) { } func (agent *SBaremetalAgent) GetAccessIP() (net.IP, error) { - ips, err := agent.GetListenIPs() - if err != nil { - return nil, err - } - if o.Options.AccessAddress == "" { - return ips[0], nil - } - for _, ip := range ips { - if ip.String() == o.Options.AccessAddress { - return ip, nil - } - } - return nil, fmt.Errorf("Not found AccessAddress %s on %s", o.Options.AccessAddress, o.Options.ListenInterface) + return agent.FindAccessIP(o.Options.AccessAddress) } func (agent *SBaremetalAgent) GetDHCPServerIP() (net.IP, error) { @@ -132,60 +91,7 @@ func (agent *SBaremetalAgent) GetDHCPServerIP() (net.IP, error) { return agent.GetDHCPServerListenIP() } -func getIfaceIPs(iface *net.Interface) ([]net.IP, error) { - addrs, err := iface.Addrs() - if err != nil { - return nil, err - } - ips := make([]net.IP, 0) - for _, a := range addrs { - if ipnet, ok := a.(*net.IPNet); ok && !ipnet.IP.IsLoopback() { - if ipnet.IP.To4() != nil { - ips = append(ips, ipnet.IP) - } - } - } - return ips, nil -} - -func (agent *SBaremetalAgent) startRegister() { - if agent.AgentId != "" { - return - } - - var delayRetryTime time.Duration = 30 * time.Second - - for { - err := agent.register() - if err != nil { - log.Errorf("Register error: %v, retry after %s...", err, delayRetryTime) - time.Sleep(delayRetryTime) - continue - } - break - } - return -} - -func (agent *SBaremetalAgent) register() error { - session := GetAdminSession() - var err error - err = agent.fetchZone(session) - if err != nil { - return err - } - err = agent.createOrUpdateBaremetalAgent(session) - if err != nil { - return err - } - log.Infof("Baremetal %s:%s register success, do offline", agent.AgentName, agent.AgentId) - err = agent.doOffline(session) - if err != nil { - return err - } - - agent.tuneSystem() - +func (agent *SBaremetalAgent) StartService() error { manager, err := NewBaremetalManager(agent) if err != nil { return fmt.Errorf("New baremetal manager error: %v", err) @@ -198,6 +104,15 @@ func (agent *SBaremetalAgent) register() error { agent.Manager = manager agent.startPXEServices(manager) + + agent.DoOnline(agent.GetAdminSession()) + return nil +} + +func (agent *SBaremetalAgent) StopService() error { + if agent.Manager != nil { + agent.Manager.Stop() + } return nil } @@ -205,167 +120,9 @@ func (agent *SBaremetalAgent) GetManager() *SBaremetalManager { return agent.Manager } -func (agent *SBaremetalAgent) getZoneByIP(session *mcclient.ClientSession) (jsonutils.JSONObject, error) { - params := jsonutils.NewDict() - listenIP, err := agent.GetListenIP() - if err != nil { - return nil, err - } - params.Add(jsonutils.NewString(listenIP.String()), "ip") - networks, err := modules.Networks.List(session, params) - if err != nil { - return nil, err - } - if len(networks.Data) == 0 { - return nil, fmt.Errorf("Not found networks by agent listen ip: %s", listenIP) - } - wireId, err := networks.Data[0].GetString("wire_id") - if err != nil { - return nil, err - } - wire, err := modules.Wires.Get(session, wireId, nil) - if err != nil { - return nil, err - } - zoneId, err := wire.GetString("zone_id") - if err != nil { - return nil, err - } - - zone, err := modules.Zones.Get(session, zoneId, nil) - if err != nil { - return nil, err - } - return zone, nil -} - -func (agent *SBaremetalAgent) fetchZone(session *mcclient.ClientSession) error { - zoneName := o.Options.Zone - var zoneInfoObj jsonutils.JSONObject - var err error - if zoneName != "" { - zoneInfoObj, err = modules.Zones.Get(session, zoneName, nil) - } else { - zoneInfoObj, err = agent.getZoneByIP(session) - } - if err != nil { - return err - } - zone := SZone{} - err = zoneInfoObj.Unmarshal(&zone) - if err != nil { - return err - } - agent.Zone = &zone - return nil -} - -func (agent *SBaremetalAgent) createOrUpdateBaremetalAgent(session *mcclient.ClientSession) error { - params := jsonutils.NewDict() - naccessIP, err := agent.GetAccessIP() - if err != nil { - return err - } - params.Add(jsonutils.NewString(naccessIP.String()), "access_ip") - ret, err := modules.Baremetalagents.List(session, params) - if err != nil { - return err - } - var ( - cloudObj jsonutils.JSONObject - agentId string - agentName string - ) - // create or update BaremetalAgent - if len(ret.Data) == 0 { - cloudObj, err = agent.createBaremetalAgent(session) - if err != nil { - return err - } - } else { - cloudBmAgent := ret.Data[0] - accessIP, _ := cloudBmAgent.GetString("access_ip") - managerUri, _ := cloudBmAgent.GetString("manager_uri") - zoneId, _ := cloudBmAgent.GetString("zone_id") - agentId, _ := cloudBmAgent.GetString("id") - if naccessIP.String() != accessIP || - agent.GetManagerUri() != managerUri || - zoneId != agent.Zone.Id { - cloudObj, err = agent.updateBaremetalAgent(session, agentId) - if err != nil { - return err - } - } else { - cloudObj = cloudBmAgent - } - } - - agentId, err = cloudObj.GetString("id") - if err != nil { - return err - } - agentName, err = cloudObj.GetString("name") - if err != nil { - return err - } - - agent.AgentId = agentId - agent.AgentName = agentName - return nil -} - -func (agent *SBaremetalAgent) GetManagerUri() string { - accessIP, _ := agent.GetAccessIP() - proto := "http" - if o.Options.EnableSsl { - proto = "https" - } - return fmt.Sprintf("%s://%s:%d", proto, accessIP, o.Options.Port) -} - -func (agent *SBaremetalAgent) getCreateUpdateInfo() (jsonutils.JSONObject, error) { - accessIP, err := agent.GetAccessIP() - if err != nil { - return nil, err - } - params := jsonutils.NewDict() - if agent.AgentId == "" { - params.Add(jsonutils.NewString(fmt.Sprintf("baremetal_%s", accessIP)), "name") - } - params.Add(jsonutils.NewString(accessIP.String()), "access_ip") - params.Add(jsonutils.NewString(agent.GetManagerUri()), "manager_uri") - params.Add(jsonutils.NewString(agent.Zone.Id), "zone_id") - return params, nil -} - -func (agent *SBaremetalAgent) createBaremetalAgent(session *mcclient.ClientSession) (jsonutils.JSONObject, error) { - params, err := agent.getCreateUpdateInfo() - if err != nil { - return nil, err - } - return modules.Baremetalagents.Create(session, params) -} - -func (agent *SBaremetalAgent) updateBaremetalAgent(session *mcclient.ClientSession, id string) (jsonutils.JSONObject, error) { - params, err := agent.getCreateUpdateInfo() - if err != nil { - return nil, err - } - return modules.Baremetalagents.Update(session, id, params) -} - -func (agent *SBaremetalAgent) doOffline(session *mcclient.ClientSession) error { - _, err := modules.Baremetalagents.PerformAction(session, agent.AgentId, "offline", nil) - return err -} - -func (agent *SBaremetalAgent) doOnline(session *mcclient.ClientSession) error { - _, err := modules.Baremetalagents.PerformAction(session, agent.AgentId, "online", nil) - return err -} - -func (agent *SBaremetalAgent) tuneSystem() { +func (agent *SBaremetalAgent) TuneSystem() error { agent.disableUDPOffloading() + return nil } func (agent *SBaremetalAgent) disableUDPOffloading() { @@ -404,8 +161,16 @@ func Start() error { if err != nil { return err } - baremetalAgent.startRegister() + return baremetalAgent.Start() +} +func Stop() error { + if baremetalAgent != nil { + log.Infof("baremetalAgent stop ...") + tmpAgent := baremetalAgent + baremetalAgent = nil + tmpAgent.Stop() + } return nil } diff --git a/pkg/baremetal/manager.go b/pkg/baremetal/manager.go index 7b77260d83..a2e77023d6 100644 --- a/pkg/baremetal/manager.go +++ b/pkg/baremetal/manager.go @@ -67,7 +67,7 @@ func (m *SBaremetalManager) killAllIPMITool() { } func (m *SBaremetalManager) GetClientSession() *mcclient.ClientSession { - return GetAdminSession() + return m.Agent.GetAdminSession() } func (m *SBaremetalManager) GetZoneId() string { diff --git a/pkg/baremetal/service/service.go b/pkg/baremetal/service/service.go index a0596ed205..9e5d8a9a32 100644 --- a/pkg/baremetal/service/service.go +++ b/pkg/baremetal/service/service.go @@ -30,6 +30,7 @@ func (s *BaremetalService) StartService() { cloudcommon.ServeForeverWithCleanup(app, &o.Options.CommonOptions, func() { tasks.OnStop() + baremetal.Stop() }) } diff --git a/pkg/cloudcommon/agent/agent.go b/pkg/cloudcommon/agent/agent.go new file mode 100644 index 0000000000..d9ec5dc630 --- /dev/null +++ b/pkg/cloudcommon/agent/agent.go @@ -0,0 +1,334 @@ +package agent + +import ( + "fmt" + "net" + "time" + + "yunion.io/x/jsonutils" + "yunion.io/x/log" + + "yunion.io/x/onecloud/pkg/mcclient" + "yunion.io/x/onecloud/pkg/mcclient/modules" +) + +type IAgent interface { + GetAgentType() string + GetAccessIP() (net.IP, error) + GetListenIP() (net.IP, error) + GetPort() int + GetEnableSsl() bool + GetZoneName() string + GetAdminSession() *mcclient.ClientSession + TuneSystem() error + StartService() error + StopService() error +} + +type SZoneInfo struct { + Name string `json:"name"` + Id string `json:"id"` +} + +type SBaseAgent struct { + virtual IAgent + + ListenInterface *net.Interface + ListenIPs []net.IP + AgentId string + AgentName string + Zone *SZoneInfo + + stop bool +} + +func getIfaceIPs(iface *net.Interface) ([]net.IP, error) { + addrs, err := iface.Addrs() + if err != nil { + return nil, err + } + ips := make([]net.IP, 0) + for _, a := range addrs { + if ipnet, ok := a.(*net.IPNet); ok && !ipnet.IP.IsLoopback() { + if ipnet.IP.To4() != nil { + ips = append(ips, ipnet.IP) + } + } + } + return ips, nil +} + +func (agent *SBaseAgent) Init(iagent IAgent, ifname string) error { + iface, err := net.InterfaceByName(ifname) + if err != nil { + return err + } + var ips []net.IP + MAX := 60 + wait := 0 + for wait < MAX { + ips, err = getIfaceIPs(iface) + if err != nil { + return err + } + if len(ips) == 0 { + time.Sleep(2 * time.Second) + wait += 2 + } else { + break + } + } + if len(ips) == 0 { + return fmt.Errorf("Interface %s ip address not found", ifname) + } + log.Debugf("Interface %s ip address: %v", iface.Name, ips) + agent.virtual = iagent + agent.ListenInterface = iface + agent.ListenIPs = ips + return nil +} + +func (agent *SBaseAgent) GetListenIPs() []net.IP { + return agent.ListenIPs +} + +func (agent *SBaseAgent) FindListenIP(listenAddr string) (net.IP, error) { + ips := agent.GetListenIPs() + if listenAddr == "" { + return ips[0], nil + } + if listenAddr == "0.0.0.0" { + return net.ParseIP(listenAddr), nil + } + for _, ip := range ips { + if ip.String() == listenAddr { + return ip, nil + } + } + return nil, fmt.Errorf("Not found Address %s on Interface %s", listenAddr, agent.ListenInterface) +} + +func (agent *SBaseAgent) FindAccessIP(accessAddr string) (net.IP, error) { + if accessAddr == "0.0.0.0" { + return nil, fmt.Errorf("Access address must be specific, should not be 0.0.0.0") + } + return agent.FindListenIP(accessAddr) +} + +func (agent *SBaseAgent) startRegister() error { + // if agent.AgentId != "" { + // return + // } + + var delayRetryTime = 30 * time.Second + var lastTry time.Time + + for !agent.stop { + if time.Now().Sub(lastTry) >= delayRetryTime { + session := agent.virtual.GetAdminSession() + err := agent.register(session) + if err == nil { + log.Infof("Register success!") + return nil + } + log.Errorf("Register error: %v, retry after %s...", err, delayRetryTime) + lastTry = time.Now() + } + time.Sleep(time.Second) + } + return fmt.Errorf("Error Stop") +} + +func (agent *SBaseAgent) register(session *mcclient.ClientSession) error { + var err error + err = agent.fetchZone(session) + if err != nil { + return err + } + err = agent.createOrUpdateBaremetalAgent(session) + if err != nil { + return err + } + log.Infof("%s %s:%s register success, do offline", agent.virtual.GetAgentType(), agent.AgentName, agent.AgentId) + err = agent.doOffline(session) + if err != nil { + return err + } + return nil +} + +func (agent *SBaseAgent) fetchZone(session *mcclient.ClientSession) error { + zoneName := agent.virtual.GetZoneName() + var zoneInfoObj jsonutils.JSONObject + var err error + if zoneName != "" { + zoneInfoObj, err = modules.Zones.Get(session, zoneName, nil) + } else { + zoneInfoObj, err = agent.getZoneByIP(session) + } + if err != nil { + return err + } + zone := SZoneInfo{} + err = zoneInfoObj.Unmarshal(&zone) + if err != nil { + return err + } + agent.Zone = &zone + return nil +} + +func (agent *SBaseAgent) getZoneByIP(session *mcclient.ClientSession) (jsonutils.JSONObject, error) { + params := jsonutils.NewDict() + listenIP, err := agent.virtual.GetListenIP() + if err != nil { + return nil, err + } + params.Add(jsonutils.NewString(listenIP.String()), "ip") + params.Add(jsonutils.JSONTrue, "is_on_premise") + networks, err := modules.Networks.List(session, params) + if err != nil { + return nil, err + } + if len(networks.Data) == 0 { + return nil, fmt.Errorf("Not found networks by agent listen ip: %s", listenIP) + } + wireId, err := networks.Data[0].GetString("wire_id") + if err != nil { + return nil, err + } + wire, err := modules.Wires.Get(session, wireId, nil) + if err != nil { + return nil, err + } + zoneId, err := wire.GetString("zone_id") + if err != nil { + return nil, err + } + + zone, err := modules.Zones.Get(session, zoneId, nil) + if err != nil { + return nil, err + } + return zone, nil +} + +func (agent *SBaseAgent) createOrUpdateBaremetalAgent(session *mcclient.ClientSession) error { + params := jsonutils.NewDict() + naccessIP, err := agent.virtual.GetAccessIP() + if err != nil { + return err + } + params.Add(jsonutils.NewString(naccessIP.String()), "access_ip") + params.Add(jsonutils.NewString(agent.virtual.GetAgentType()), "agent_type") + ret, err := modules.Baremetalagents.List(session, params) + if err != nil { + return err + } + var ( + cloudObj jsonutils.JSONObject + agentId string + agentName string + ) + // create or update BaremetalAgent + if len(ret.Data) == 0 { + cloudObj, err = agent.createBaremetalAgent(session) + if err != nil { + return err + } + } else { + cloudBmAgent := ret.Data[0] + accessIP, _ := cloudBmAgent.GetString("access_ip") + managerUri, _ := cloudBmAgent.GetString("manager_uri") + zoneId, _ := cloudBmAgent.GetString("zone_id") + agentId, _ := cloudBmAgent.GetString("id") + if naccessIP.String() != accessIP || + agent.GetManagerUri() != managerUri || + zoneId != agent.Zone.Id { + cloudObj, err = agent.updateBaremetalAgent(session, agentId) + if err != nil { + return err + } + } else { + cloudObj = cloudBmAgent + } + } + + agentId, err = cloudObj.GetString("id") + if err != nil { + return err + } + agentName, err = cloudObj.GetString("name") + if err != nil { + return err + } + + agent.AgentId = agentId + agent.AgentName = agentName + return nil +} + +func (agent *SBaseAgent) GetManagerUri() string { + accessIP, _ := agent.virtual.GetAccessIP() + proto := "http" + if agent.virtual.GetEnableSsl() { + proto = "https" + } + return fmt.Sprintf("%s://%s:%d", proto, accessIP, agent.virtual.GetPort()) +} + +func (agent *SBaseAgent) getCreateUpdateInfo() (jsonutils.JSONObject, error) { + accessIP, err := agent.virtual.GetAccessIP() + if err != nil { + return nil, err + } + params := jsonutils.NewDict() + if agent.AgentId == "" { + params.Add(jsonutils.NewString(fmt.Sprintf("%s_%s", agent.virtual.GetAgentType(), accessIP)), "name") + } + params.Add(jsonutils.NewString(accessIP.String()), "access_ip") + params.Add(jsonutils.NewString(agent.GetManagerUri()), "manager_uri") + params.Add(jsonutils.NewString(agent.Zone.Id), "zone_id") + params.Add(jsonutils.NewString(agent.virtual.GetAgentType()), "agent_type") + return params, nil +} + +func (agent *SBaseAgent) createBaremetalAgent(session *mcclient.ClientSession) (jsonutils.JSONObject, error) { + params, err := agent.getCreateUpdateInfo() + if err != nil { + return nil, err + } + return modules.Baremetalagents.Create(session, params) +} + +func (agent *SBaseAgent) updateBaremetalAgent(session *mcclient.ClientSession, id string) (jsonutils.JSONObject, error) { + params, err := agent.getCreateUpdateInfo() + if err != nil { + return nil, err + } + return modules.Baremetalagents.Update(session, id, params) +} + +func (agent *SBaseAgent) doOffline(session *mcclient.ClientSession) error { + _, err := modules.Baremetalagents.PerformAction(session, agent.AgentId, "offline", nil) + return err +} + +func (agent *SBaseAgent) DoOnline(session *mcclient.ClientSession) error { + _, err := modules.Baremetalagents.PerformAction(session, agent.AgentId, "online", nil) + return err +} + +func (agent *SBaseAgent) Start() error { + err := agent.startRegister() + if err != nil { + return err + } + agent.virtual.TuneSystem() + return agent.virtual.StartService() +} + +func (agent *SBaseAgent) Stop() { + agent.stop = true + agent.virtual.StopService() +} From 2c022cd0ef97aaf01229ce211d9d14dfdb0af371 Mon Sep 17 00:00:00 2001 From: Qiu Jian Date: Tue, 19 Mar 2019 20:18:48 +0800 Subject: [PATCH 02/10] fix: make virtual interface pointer more general --- pkg/cloudcommon/agent/agent.go | 36 +++++++++++++++++++--------------- 1 file changed, 20 insertions(+), 16 deletions(-) diff --git a/pkg/cloudcommon/agent/agent.go b/pkg/cloudcommon/agent/agent.go index d9ec5dc630..1bf68d3e61 100644 --- a/pkg/cloudcommon/agent/agent.go +++ b/pkg/cloudcommon/agent/agent.go @@ -31,7 +31,7 @@ type SZoneInfo struct { } type SBaseAgent struct { - virtual IAgent + virtual interface{} ListenInterface *net.Interface ListenIPs []net.IP @@ -58,6 +58,10 @@ func getIfaceIPs(iface *net.Interface) ([]net.IP, error) { return ips, nil } +func (agent *SBaseAgent) IAgent() IAgent { + return agent.virtual.(IAgent) +} + func (agent *SBaseAgent) Init(iagent IAgent, ifname string) error { iface, err := net.InterfaceByName(ifname) if err != nil { @@ -125,7 +129,7 @@ func (agent *SBaseAgent) startRegister() error { for !agent.stop { if time.Now().Sub(lastTry) >= delayRetryTime { - session := agent.virtual.GetAdminSession() + session := agent.IAgent().GetAdminSession() err := agent.register(session) if err == nil { log.Infof("Register success!") @@ -149,7 +153,7 @@ func (agent *SBaseAgent) register(session *mcclient.ClientSession) error { if err != nil { return err } - log.Infof("%s %s:%s register success, do offline", agent.virtual.GetAgentType(), agent.AgentName, agent.AgentId) + log.Infof("%s %s:%s register success, do offline", agent.IAgent().GetAgentType(), agent.AgentName, agent.AgentId) err = agent.doOffline(session) if err != nil { return err @@ -158,7 +162,7 @@ func (agent *SBaseAgent) register(session *mcclient.ClientSession) error { } func (agent *SBaseAgent) fetchZone(session *mcclient.ClientSession) error { - zoneName := agent.virtual.GetZoneName() + zoneName := agent.IAgent().GetZoneName() var zoneInfoObj jsonutils.JSONObject var err error if zoneName != "" { @@ -180,7 +184,7 @@ func (agent *SBaseAgent) fetchZone(session *mcclient.ClientSession) error { func (agent *SBaseAgent) getZoneByIP(session *mcclient.ClientSession) (jsonutils.JSONObject, error) { params := jsonutils.NewDict() - listenIP, err := agent.virtual.GetListenIP() + listenIP, err := agent.IAgent().GetListenIP() if err != nil { return nil, err } @@ -215,12 +219,12 @@ func (agent *SBaseAgent) getZoneByIP(session *mcclient.ClientSession) (jsonutils func (agent *SBaseAgent) createOrUpdateBaremetalAgent(session *mcclient.ClientSession) error { params := jsonutils.NewDict() - naccessIP, err := agent.virtual.GetAccessIP() + naccessIP, err := agent.IAgent().GetAccessIP() if err != nil { return err } params.Add(jsonutils.NewString(naccessIP.String()), "access_ip") - params.Add(jsonutils.NewString(agent.virtual.GetAgentType()), "agent_type") + params.Add(jsonutils.NewString(agent.IAgent().GetAgentType()), "agent_type") ret, err := modules.Baremetalagents.List(session, params) if err != nil { return err @@ -269,27 +273,27 @@ func (agent *SBaseAgent) createOrUpdateBaremetalAgent(session *mcclient.ClientSe } func (agent *SBaseAgent) GetManagerUri() string { - accessIP, _ := agent.virtual.GetAccessIP() + accessIP, _ := agent.IAgent().GetAccessIP() proto := "http" - if agent.virtual.GetEnableSsl() { + if agent.IAgent().GetEnableSsl() { proto = "https" } - return fmt.Sprintf("%s://%s:%d", proto, accessIP, agent.virtual.GetPort()) + return fmt.Sprintf("%s://%s:%d", proto, accessIP, agent.IAgent().GetPort()) } func (agent *SBaseAgent) getCreateUpdateInfo() (jsonutils.JSONObject, error) { - accessIP, err := agent.virtual.GetAccessIP() + accessIP, err := agent.IAgent().GetAccessIP() if err != nil { return nil, err } params := jsonutils.NewDict() if agent.AgentId == "" { - params.Add(jsonutils.NewString(fmt.Sprintf("%s_%s", agent.virtual.GetAgentType(), accessIP)), "name") + params.Add(jsonutils.NewString(fmt.Sprintf("%s_%s", agent.IAgent().GetAgentType(), accessIP)), "name") } params.Add(jsonutils.NewString(accessIP.String()), "access_ip") params.Add(jsonutils.NewString(agent.GetManagerUri()), "manager_uri") params.Add(jsonutils.NewString(agent.Zone.Id), "zone_id") - params.Add(jsonutils.NewString(agent.virtual.GetAgentType()), "agent_type") + params.Add(jsonutils.NewString(agent.IAgent().GetAgentType()), "agent_type") return params, nil } @@ -324,11 +328,11 @@ func (agent *SBaseAgent) Start() error { if err != nil { return err } - agent.virtual.TuneSystem() - return agent.virtual.StartService() + agent.IAgent().TuneSystem() + return agent.IAgent().StartService() } func (agent *SBaseAgent) Stop() { agent.stop = true - agent.virtual.StopService() + agent.IAgent().StopService() } From a93968e0d2590a3cad7ee3eccc248e6ef7ddc13c Mon Sep 17 00:00:00 2001 From: Qiu Jian Date: Wed, 20 Mar 2019 14:37:37 +0800 Subject: [PATCH 03/10] fix: 1. vmware detch disk logic error 2. host add storage commit_rate 3. host enable netif failed if a fake ipaddr is given --- pkg/compute/guestdrivers/base.go | 4 ++ pkg/compute/guestdrivers/esxi.go | 28 ++++++------- pkg/compute/guestdrivers/managedvirtual.go | 49 +--------------------- pkg/compute/models/hosts.go | 43 ++++++++++++++----- pkg/compute/models/storages.go | 28 ++++++------- pkg/compute/tasks/guest_sync_task.go | 1 + pkg/util/esxi/storage.go | 13 ++++++ pkg/util/esxi/virtualmachine.go | 8 +++- 8 files changed, 87 insertions(+), 87 deletions(-) diff --git a/pkg/compute/guestdrivers/base.go b/pkg/compute/guestdrivers/base.go index 5321e6c8c2..dea371d62d 100644 --- a/pkg/compute/guestdrivers/base.go +++ b/pkg/compute/guestdrivers/base.go @@ -235,3 +235,7 @@ func (self *SBaseGuestDriver) IsSupportEip() bool { func (self *SBaseGuestDriver) NeedStopForChangeSpec() bool { return true } + +func (self *SBaseGuestDriver) RequestSyncConfigOnHost(ctx context.Context, guest *models.SGuest, host *models.SHost, task taskman.ITask) error { + return fmt.Errorf("SBaseGuestDriver: Not Implement") +} diff --git a/pkg/compute/guestdrivers/esxi.go b/pkg/compute/guestdrivers/esxi.go index ebc2abbb6b..e83ee8e7fb 100644 --- a/pkg/compute/guestdrivers/esxi.go +++ b/pkg/compute/guestdrivers/esxi.go @@ -38,10 +38,10 @@ func (self *SESXiGuestDriver) GetMinimalSysDiskSizeGb() int { return options.Options.DefaultDiskSizeMB / 1024 } -func (self *SESXiGuestDriver) RequestSyncConfigOnHost(ctx context.Context, guest *models.SGuest, host *models.SHost, task taskman.ITask) error { - task.ScheduleRun(nil) - return nil -} +// func (self *SESXiGuestDriver) RequestSyncConfigOnHost(ctx context.Context, guest *models.SGuest, host *models.SHost, task taskman.ITask) error { +// task.ScheduleRun(nil) +// return nil +// } func (self *SESXiGuestDriver) GetMaxSecurityGroupCount() int { //暂不支持绑定安全组 @@ -49,11 +49,11 @@ func (self *SESXiGuestDriver) GetMaxSecurityGroupCount() int { } func (self *SESXiGuestDriver) GetDetachDiskStatus() ([]string, error) { - return []string{models.VM_READY}, nil + return []string{models.VM_READY, models.VM_RUNNING}, nil } func (self *SESXiGuestDriver) GetAttachDiskStatus() ([]string, error) { - return []string{models.VM_READY}, nil + return []string{models.VM_READY, models.VM_RUNNING}, nil } func (self *SESXiGuestDriver) GetChangeConfigStatus() ([]string, error) { @@ -64,14 +64,14 @@ func (self *SESXiGuestDriver) CanKeepDetachDisk() bool { return false } -func (self *SESXiGuestDriver) RequestDeleteDetachedDisk(ctx context.Context, disk *models.SDisk, task taskman.ITask, isPurge bool) error { - err := disk.RealDelete(ctx, task.GetUserCred()) - if err != nil { - return err - } - task.ScheduleRun(nil) - return nil -} +// func (self *SESXiGuestDriver) RequestDeleteDetachedDisk(ctx context.Context, disk *models.SDisk, task taskman.ITask, isPurge bool) error { +// err := disk.RealDelete(ctx, task.GetUserCred()) +// if err != nil { +// return err +// } +// task.ScheduleRun(nil) +// return nil +// } func (self *SESXiGuestDriver) RequestGuestHotAddIso(ctx context.Context, guest *models.SGuest, path string, task taskman.ITask) error { task.ScheduleRun(nil) diff --git a/pkg/compute/guestdrivers/managedvirtual.go b/pkg/compute/guestdrivers/managedvirtual.go index 6cbadcac2a..7c5f86a6cc 100644 --- a/pkg/compute/guestdrivers/managedvirtual.go +++ b/pkg/compute/guestdrivers/managedvirtual.go @@ -497,7 +497,7 @@ func (self *SManagedVirtualizedGuestDriver) RequestSyncConfigOnHost(ctx context. return nil, err } for _, disk := range removed { - if err := iVM.DetachDisk(ctx, disk.GetId()); err != nil { + if err := iVM.DetachDisk(ctx, disk.GetGlobalId()); err != nil { return nil, err } } @@ -511,53 +511,6 @@ func (self *SManagedVirtualizedGuestDriver) RequestSyncConfigOnHost(ctx context. return nil } -/*func (self *SManagedVirtualizedGuestDriver) RequestSyncConfigOnHost(ctx context.Context, guest *models.SGuest, host *models.SHost, task taskman.ITask) error { - taskman.LocalTaskRun(task, func() (jsonutils.JSONObject, error) { - if ihost, err := host.GetIHost(); err != nil { - return nil, err - } else if iVM, err := ihost.GetIVMById(guest.ExternalId); err != nil { - return nil, err - } else { - if fw_only, _ := task.GetParams().Bool("fw_only"); fw_only { - if err := iVM.SyncSecurityGroup(guest.SecgrpId, guest.GetSecgroupName(), guest.GetSecRules()); err != nil { - return nil, err - } - } else { - if iDisks, err := iVM.GetIDisks(); err != nil { - return nil, err - } else { - disks := make([]models.SDisk, 0) - for _, guestdisk := range guest.GetDisks() { - disk := guestdisk.GetDisk() - disks = append(disks, *disk) - } - - added := make([]models.SDisk, 0) - commondb := make([]models.SDisk, 0) - commonext := make([]cloudprovider.ICloudDisk, 0) - removed := make([]cloudprovider.ICloudDisk, 0) - - if err := compare.CompareSets(disks, iDisks, &added, &commondb, &commonext, &removed); err != nil { - return nil, err - } - for _, disk := range removed { - if err := iVM.DetachDisk(ctx, disk.GetId()); err != nil { - return nil, err - } - } - for _, disk := range added { - if err := iVM.AttachDisk(ctx, disk.ExternalId); err != nil { - return nil, err - } - } - } - } - } - return nil, nil - }) - return nil -}*/ - func (self *SManagedVirtualizedGuestDriver) RequestRenewInstance(guest *models.SGuest, bc billing.SBillingCycle) (time.Time, error) { iVM, err := guest.GetIVM() if err != nil { diff --git a/pkg/compute/models/hosts.go b/pkg/compute/models/hosts.go index e1556945bd..aaca90f080 100644 --- a/pkg/compute/models/hosts.go +++ b/pkg/compute/models/hosts.go @@ -955,24 +955,43 @@ func (self *SHost) GetHardwareSpecification() *jsonutils.JSONDict { } type SStorageCapacity struct { - Capacity int - Used int - Wasted int - VCapacity int + Capacity int `json:"capacity,omitzero"` + Used int `json:"used_capacity,omitzero"` + Wasted int `json:"waste_capacity,omitzero"` + VCapacity int `json:"virtual_capacity,omitzero"` } -func (capa SStorageCapacity) GetFree() int { - return capa.VCapacity - capa.Used - capa.Wasted +func (cap *SStorageCapacity) GetFree() int { + return cap.VCapacity - cap.Used - cap.Wasted +} + +func (cap *SStorageCapacity) GetCommitRate() float64 { + if cap.Capacity > 0 { + return float64(int(float64(cap.Used)*100.0/float64(cap.Capacity)+0.5) / 100.0) + } else { + return 0.0 + } +} + +func (cap *SStorageCapacity) Add(cap2 SStorageCapacity) { + cap.Capacity += cap2.Capacity + cap.Used += cap2.Used + cap.Wasted += cap2.Wasted + cap.VCapacity += cap2.VCapacity +} + +func (cap *SStorageCapacity) ToJson() *jsonutils.JSONDict { + ret := jsonutils.Marshal(cap).(*jsonutils.JSONDict) + ret.Add(jsonutils.NewFloat(cap.GetCommitRate()), "commit_rate") + ret.Add(jsonutils.NewInt(int64(cap.GetFree())), "free_capacity") + return ret } func (self *SHost) GetAttachedStorageCapacity() SStorageCapacity { ret := SStorageCapacity{} storages := self.GetAttachedStorages("") for _, s := range storages { - ret.Capacity += s.GetCapacity() - ret.Used += s.GetUsedCapacity(tristate.True) - ret.Wasted += s.GetUsedCapacity(tristate.False) - ret.VCapacity += int(float32(s.GetCapacity()) * s.GetOvercommitBound()) + ret.Add(s.getStorageCapacity()) } return ret } @@ -2140,6 +2159,7 @@ func (self *SHost) getMoreDetails(ctx context.Context, extra *jsonutils.JSONDict extra.Add(jsonutils.NewInt(int64(capa.Wasted)), "storage_waste") extra.Add(jsonutils.NewInt(int64(capa.VCapacity)), "storage_virtual") extra.Add(jsonutils.NewInt(int64(capa.GetFree())), "storage_free") + extra.Add(jsonutils.NewFloat(capa.GetCommitRate()), "storage_commit_rate") extra.Add(self.GetHardwareSpecification(), "spec") // extra = self.SManagedResourceBase.getExtraDetails(ctx, extra) @@ -2985,6 +3005,9 @@ func (self *SHost) EnableNetif(ctx context.Context, userCred mcclient.TokenCrede } else if requireDesignatedIp { log.Errorf("Cannot allocate IP %s, not reachable", ipAddr) return fmt.Errorf("Cannot allocate IP %s, not reachable", ipAddr) + } else { + // the ipaddr is not usable, should be reset to empty + ipAddr = "" } } wire := netif.GetWire() diff --git a/pkg/compute/models/storages.go b/pkg/compute/models/storages.go index 9f5ec361d2..d8f3537219 100644 --- a/pkg/compute/models/storages.go +++ b/pkg/compute/models/storages.go @@ -338,21 +338,21 @@ func (self *SStorage) GetStorageCachePath(mountPoint, imageCachePath string) str } } +func (self *SStorage) getStorageCapacity() SStorageCapacity { + capa := SStorageCapacity{} + + capa.Capacity = self.GetCapacity() + capa.Used = self.GetUsedCapacity(tristate.True) + capa.Wasted = self.GetUsedCapacity(tristate.False) + capa.VCapacity = int(float32(self.GetCapacity()) * self.GetOvercommitBound()) + + return capa +} + func (self *SStorage) getMoreDetails(extra *jsonutils.JSONDict) *jsonutils.JSONDict { - used := self.GetUsedCapacity(tristate.True) - waste := self.GetUsedCapacity(tristate.False) - vcapa := float32(self.GetCapacity()) * self.GetOvercommitBound() - extra.Add(jsonutils.NewInt(int64(used)), "used_capacity") - extra.Add(jsonutils.NewInt(int64(waste)), "waste_capacity") - extra.Add(jsonutils.NewFloat(float64(vcapa)), "virtual_capacity") - extra.Add(jsonutils.NewFloat(float64(vcapa-float32(used)-float32(waste))), "free_capacity") - if self.GetCapacity() > 0 { - value := float64(used * 1.0 / self.GetCapacity()) - value = float64(int(value*100+0.5) / 100.0) - extra.Add(jsonutils.NewFloat(value), "commit_rate") - } else { - extra.Add(jsonutils.NewFloat(0.0), "commit_rate") - } + capa := self.getStorageCapacity() + extra.Update(capa.ToJson()) + extra.Add(jsonutils.NewFloat(float64(self.GetOvercommitBound())), "commit_bound") info := self.getCloudProviderInfo() diff --git a/pkg/compute/tasks/guest_sync_task.go b/pkg/compute/tasks/guest_sync_task.go index 710abfe646..6d853cbba3 100644 --- a/pkg/compute/tasks/guest_sync_task.go +++ b/pkg/compute/tasks/guest_sync_task.go @@ -40,6 +40,7 @@ func (self *GuestSyncConfTask) OnSyncComplete(ctx context.Context, obj db.IStand db.OpsLog.LogEvent(guest, db.ACT_SYNC_CONF, nil, self.UserCred) self.SetStageComplete(ctx, guest.GetShortDesc(ctx)) } else if data.Contains("task") { + // XXX this is only applied to KVM, which will call task_complete twice self.SetStage("on_disk_sync_complete", nil) } else { self.OnDiskSyncComplete(ctx, guest, data) diff --git a/pkg/util/esxi/storage.go b/pkg/util/esxi/storage.go index 2cca88c460..20979e3bd3 100644 --- a/pkg/util/esxi/storage.go +++ b/pkg/util/esxi/storage.go @@ -683,3 +683,16 @@ func (self *SDatastore) RemoveDir(ctx context.Context, remotePath string) error func (self *SDatastore) IsSysDiskStore() bool { return true } + +func (self *SDatastore) MoveVmdk(ctx context.Context, srcPath string, dstPath string) error { + dm := object.NewVirtualDiskManager(self.manager.client.Client) + defer dm.Destroy(ctx) + + srcUrl := self.GetPathUrl(srcPath) + dstUrl := self.GetPathUrl(dstPath) + task, err := dm.MoveVirtualDisk(ctx, srcUrl, nil, dstUrl, nil, true) + if err != nil { + return err + } + return task.Wait(ctx) +} diff --git a/pkg/util/esxi/virtualmachine.go b/pkg/util/esxi/virtualmachine.go index 6ccdad303a..938b9eba1d 100644 --- a/pkg/util/esxi/virtualmachine.go +++ b/pkg/util/esxi/virtualmachine.go @@ -120,8 +120,14 @@ func (self *SVirtualMachine) UpdateVM(ctx context.Context, name string) error { return cloudprovider.ErrNotImplemented } +// TODO: detach disk to a separate directory, so as to keep disk independent of VM + func (self *SVirtualMachine) DetachDisk(ctx context.Context, diskId string) error { - return cloudprovider.ErrNotImplemented + vdisk, err := self.GetIDiskById(diskId) + if err != nil { + return err + } + return self.doDetachDisk(ctx, vdisk.(*SVirtualDisk), false) } func (self *SVirtualMachine) AttachDisk(ctx context.Context, diskId string) error { From 786db8f87f272d619f3fb3ede853b8c86fca371b Mon Sep 17 00:00:00 2001 From: wanyaoqi Date: Wed, 20 Mar 2019 15:44:48 +0800 Subject: [PATCH 04/10] fix secgroup find by id or name --- pkg/compute/models/guests.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkg/compute/models/guests.go b/pkg/compute/models/guests.go index 579453d8e2..392bf7e1f5 100644 --- a/pkg/compute/models/guests.go +++ b/pkg/compute/models/guests.go @@ -350,7 +350,7 @@ func (manager *SGuestManager) ListItemFilter(ctx context.Context, q *sqlchemy.SQ secgrpFilter = secgrpFilter[1:] notIn = true } - secgrp, _ := SecurityGroupManager.FetchByIdOrName(nil, secgrpFilter) + secgrp, _ := SecurityGroupManager.FetchByIdOrName(userCred, secgrpFilter) if secgrp == nil { return nil, httperrors.NewResourceNotFoundError("secgroup %s not found", secgrpFilter) } From 1ed92bd1765144f4114d18646cd804a57ac2737c Mon Sep 17 00:00:00 2001 From: Zexi Li Date: Wed, 20 Mar 2019 18:15:02 +0800 Subject: [PATCH 05/10] fix: gpu server not work --- pkg/apis/compute/api.go | 2 +- pkg/apis/compute/guest_const.go | 162 ++++++++++ pkg/cloudcommon/cmdline/helper.go | 3 + pkg/cloudcommon/cmdline/helper_test.go | 11 +- pkg/compute/models/guests.go | 281 +++++++++--------- pkg/compute/models/isolated_devices.go | 3 +- pkg/compute/models/networks.go | 24 +- pkg/hostman/guestman/qemu-kvmhelper.go | 10 +- .../isolated_device/isolated_device.go | 7 +- pkg/mcclient/options/schedulers.go | 2 +- pkg/mcclient/options/servers.go | 3 +- pkg/scheduler/cache/candidate/hosts.go | 84 ++++-- 12 files changed, 402 insertions(+), 190 deletions(-) create mode 100644 pkg/apis/compute/guest_const.go diff --git a/pkg/apis/compute/api.go b/pkg/apis/compute/api.go index 90a67806fc..a753ce43a5 100644 --- a/pkg/apis/compute/api.go +++ b/pkg/apis/compute/api.go @@ -142,7 +142,7 @@ type ServerCreateInput struct { Description string `json:"description"` BootOrder string `json:"boot_order"` ResetPassword *bool `json:"reset_password"` - DisableDelete bool `json:"disable_delete"` + DisableDelete *bool `json:"disable_delete"` ShutdownBehavior string `json:"shutdown_behavior"` AutoStart bool `json:"auto_start"` DeployConfigs []*DeployConfig `json:"deploy_configs"` diff --git a/pkg/apis/compute/guest_const.go b/pkg/apis/compute/guest_const.go new file mode 100644 index 0000000000..e634210f7c --- /dev/null +++ b/pkg/apis/compute/guest_const.go @@ -0,0 +1,162 @@ +package compute + +const ( + VM_INIT = "init" + VM_UNKNOWN = "unknown" + VM_SCHEDULE = "schedule" + VM_SCHEDULE_FAILED = "sched_fail" + VM_CREATE_NETWORK = "network" + VM_NETWORK_FAILED = "net_fail" + VM_DEVICE_FAILED = "dev_fail" + VM_CREATE_FAILED = "create_fail" + VM_CREATE_DISK = "disk" + VM_DISK_FAILED = "disk_fail" + VM_START_DEPLOY = "start_deploy" + VM_DEPLOYING = "deploying" + VM_DEPLOY_FAILED = "deploy_fail" + VM_READY = "ready" + VM_START_START = "start_start" + VM_STARTING = "starting" + VM_START_FAILED = "start_fail" // # = ready + VM_RUNNING = "running" + VM_START_STOP = "start_stop" + VM_STOPPING = "stopping" + VM_STOP_FAILED = "stop_fail" // # = running + + VM_BACKUP_STARTING = "backup_starting" + VM_BACKUP_CREATING = "backup_creating" + VM_BACKUP_CREATE_FAILED = "backup_create_fail" + VM_DEPLOYING_BACKUP = "deploying_backup" + VM_DEPLOYING_BACKUP_FAILED = "deploging_backup_fail" + VM_DELETING_BACKUP = "deleting_backup" + VM_BACKUP_DELETE_FAILED = "backup_delete_fail" + VM_SWITCH_TO_BACKUP = "switch_to_backup" + VM_SWITCH_TO_BACKUP_FAILED = "switch_to_backup_fail" + + VM_ATTACH_DISK_FAILED = "attach_disk_fail" + VM_DETACH_DISK_FAILED = "detach_disk_fail" + + VM_START_SUSPEND = "start_suspend" + VM_SUSPENDING = "suspending" + VM_SUSPEND = "suspend" + VM_SUSPEND_FAILED = "suspend_failed" + + VM_START_DELETE = "start_delete" + VM_DELETE_FAIL = "delete_fail" + VM_DELETING = "deleting" + + VM_DEALLOCATED = "deallocated" + + VM_START_MIGRATE = "start_migrate" + VM_MIGRATING = "migrating" + VM_MIGRATE_FAILED = "migrate_failed" + + VM_CHANGE_FLAVOR = "change_flavor" + VM_CHANGE_FLAVOR_FAIL = "change_flavor_fail" + VM_REBUILD_ROOT = "rebuild_root" + VM_REBUILD_ROOT_FAIL = "rebuild_root_fail" + + VM_START_SNAPSHOT = "snapshot_start" + VM_SNAPSHOT = "snapshot" + VM_SNAPSHOT_DELETE = "snapshot_delete" + VM_BLOCK_STREAM = "block_stream" + VM_MIRROR_FAIL = "mirror_failed" + VM_SNAPSHOT_SUCC = "snapshot_succ" + VM_SNAPSHOT_FAILED = "snapshot_failed" + + VM_SYNCING_STATUS = "syncing" + VM_SYNC_CONFIG = "sync_config" + VM_SYNC_FAIL = "sync_fail" + + VM_RESIZE_DISK = "resize_disk" + VM_RESIZE_DISK_FAILED = "resize_disk_fail" + VM_START_SAVE_DISK = "start_save_disk" + VM_SAVE_DISK = "save_disk" + VM_SAVE_DISK_FAILED = "save_disk_failed" + + VM_RESTORING_SNAPSHOT = "restoring_snapshot" + VM_RESTORE_DISK = "restore_disk" + VM_RESTORE_STATE = "restore_state" + VM_RESTORE_FAILED = "restore_failed" + + VM_ASSOCIATE_EIP = "associate_eip" + VM_ASSOCIATE_EIP_FAILED = "associate_eip_failed" + VM_DISSOCIATE_EIP = "dissociate_eip" + VM_DISSOCIATE_EIP_FAILED = "dissociate_eip_failed" + + VM_REMOVE_STATEFILE = "remove_state" + + VM_ADMIN = "admin" + + SHUTDOWN_STOP = "stop" + SHUTDOWN_TERMINATE = "terminate" + + HYPERVISOR_KVM = "kvm" + HYPERVISOR_CONTAINER = "container" + HYPERVISOR_BAREMETAL = "baremetal" + HYPERVISOR_ESXI = "esxi" + HYPERVISOR_HYPERV = "hyperv" + HYPERVISOR_XEN = "xen" + + HYPERVISOR_ALIYUN = "aliyun" + HYPERVISOR_QCLOUD = "qcloud" + HYPERVISOR_AZURE = "azure" + HYPERVISOR_AWS = "aws" + HYPERVISOR_HUAWEI = "huawei" + HYPERVISOR_OPENSTACK = "openstack" + + // HYPERVISOR_DEFAULT = HYPERVISOR_KVM + HYPERVISOR_DEFAULT = HYPERVISOR_KVM +) + +var VM_RUNNING_STATUS = []string{VM_START_START, VM_STARTING, VM_RUNNING, VM_BLOCK_STREAM} +var VM_CREATING_STATUS = []string{VM_CREATE_NETWORK, VM_CREATE_DISK, VM_START_DEPLOY, VM_DEPLOYING} + +var HYPERVISORS = []string{HYPERVISOR_KVM, + HYPERVISOR_BAREMETAL, + HYPERVISOR_ESXI, + HYPERVISOR_CONTAINER, + HYPERVISOR_ALIYUN, + HYPERVISOR_AZURE, + HYPERVISOR_AWS, + HYPERVISOR_QCLOUD, + HYPERVISOR_HUAWEI, + HYPERVISOR_OPENSTACK, +} + +var PUBLIC_CLOUD_HYPERVISORS = []string{ + HYPERVISOR_ALIYUN, + HYPERVISOR_AWS, + HYPERVISOR_AZURE, + HYPERVISOR_QCLOUD, + HYPERVISOR_HUAWEI, + HYPERVISOR_OPENSTACK, +} + +// var HYPERVISORS = []string{HYPERVISOR_ALIYUN} + +var HYPERVISOR_HOSTTYPE = map[string]string{ + HYPERVISOR_KVM: HOST_TYPE_HYPERVISOR, + HYPERVISOR_BAREMETAL: HOST_TYPE_BAREMETAL, + HYPERVISOR_ESXI: HOST_TYPE_ESXI, + HYPERVISOR_CONTAINER: HOST_TYPE_KUBELET, + HYPERVISOR_ALIYUN: HOST_TYPE_ALIYUN, + HYPERVISOR_AZURE: HOST_TYPE_AZURE, + HYPERVISOR_AWS: HOST_TYPE_AWS, + HYPERVISOR_QCLOUD: HOST_TYPE_QCLOUD, + HYPERVISOR_HUAWEI: HOST_TYPE_HUAWEI, + HYPERVISOR_OPENSTACK: HOST_TYPE_OPENSTACK, +} + +var HOSTTYPE_HYPERVISOR = map[string]string{ + HOST_TYPE_HYPERVISOR: HYPERVISOR_KVM, + HOST_TYPE_BAREMETAL: HYPERVISOR_BAREMETAL, + HOST_TYPE_ESXI: HYPERVISOR_ESXI, + HOST_TYPE_KUBELET: HYPERVISOR_CONTAINER, + HOST_TYPE_ALIYUN: HYPERVISOR_ALIYUN, + HOST_TYPE_AZURE: HYPERVISOR_AZURE, + HOST_TYPE_AWS: HYPERVISOR_AWS, + HOST_TYPE_QCLOUD: HYPERVISOR_QCLOUD, + HOST_TYPE_HUAWEI: HYPERVISOR_HUAWEI, + HOST_TYPE_OPENSTACK: HYPERVISOR_OPENSTACK, +} diff --git a/pkg/cloudcommon/cmdline/helper.go b/pkg/cloudcommon/cmdline/helper.go index ee9184c081..fffe148834 100644 --- a/pkg/cloudcommon/cmdline/helper.go +++ b/pkg/cloudcommon/cmdline/helper.go @@ -310,6 +310,9 @@ func FetchScheduleInputByJSON(obj jsonutils.JSONObject) (*scheduler.ScheduleInpu if err != nil { return nil, err } + if obj.Contains("scheduler") { + obj, _ = obj.Get("scheduler") + } conf := &input.ServerConfig conf.ServerConfigs, err = FetchServerConfigsByJSON(obj) if err != nil { diff --git a/pkg/cloudcommon/cmdline/helper_test.go b/pkg/cloudcommon/cmdline/helper_test.go index a03e8c62ed..2391633f4d 100644 --- a/pkg/cloudcommon/cmdline/helper_test.go +++ b/pkg/cloudcommon/cmdline/helper_test.go @@ -23,9 +23,10 @@ func TestFetchServerConfigsByJSON(t *testing.T) { args: args{jsonutils.Marshal(map[string]string{ "disk.0": "1g:centos", "net.0": "192.168.222.3:inf0", + "net.1": "[random]", "schedtag.0": "ssd:require", "schedtag.1": "container:exclude", - "isolated_device.0": "vendor=nvidia:p400", + "isolated_device.0": "vendor=NVIDIA:GeForce GTX 1050 Ti", "baremetal_disk_config.0": "raid0:[1,2]", })}, want: &compute.ServerConfigs{ @@ -42,6 +43,10 @@ func TestFetchServerConfigsByJSON(t *testing.T) { Network: "inf0", Address: "192.168.222.3", }, + { + Index: 1, + Exit: false, + }, }, Schedtags: []*compute.SchedtagConfig{ { @@ -55,8 +60,8 @@ func TestFetchServerConfigsByJSON(t *testing.T) { }, IsolatedDevices: []*compute.IsolatedDeviceConfig{ { - Vendor: "nvidia", - Model: "p400", + Vendor: "NVIDIA", + Model: "GeForce GTX 1050 Ti", }, }, BaremetalDiskConfigs: []*compute.BaremetalDiskConfig{ diff --git a/pkg/compute/models/guests.go b/pkg/compute/models/guests.go index c5f05155de..7663107220 100644 --- a/pkg/compute/models/guests.go +++ b/pkg/compute/models/guests.go @@ -41,165 +41,126 @@ import ( ) const ( - VM_INIT = "init" - VM_UNKNOWN = "unknown" - VM_SCHEDULE = "schedule" - VM_SCHEDULE_FAILED = "sched_fail" - VM_CREATE_NETWORK = "network" - VM_NETWORK_FAILED = "net_fail" - VM_DEVICE_FAILED = "dev_fail" - VM_CREATE_FAILED = "create_fail" - VM_CREATE_DISK = "disk" - VM_DISK_FAILED = "disk_fail" - VM_START_DEPLOY = "start_deploy" - VM_DEPLOYING = "deploying" - VM_DEPLOY_FAILED = "deploy_fail" - VM_READY = "ready" - VM_START_START = "start_start" - VM_STARTING = "starting" - VM_START_FAILED = "start_fail" // # = ready - VM_RUNNING = "running" - VM_START_STOP = "start_stop" - VM_STOPPING = "stopping" - VM_STOP_FAILED = "stop_fail" // # = running + VM_INIT = api.VM_INIT + VM_UNKNOWN = api.VM_UNKNOWN + VM_SCHEDULE = api.VM_SCHEDULE + VM_SCHEDULE_FAILED = api.VM_SCHEDULE_FAILED + VM_CREATE_NETWORK = api.VM_CREATE_NETWORK + VM_NETWORK_FAILED = api.VM_NETWORK_FAILED + VM_DEVICE_FAILED = api.VM_DEVICE_FAILED + VM_CREATE_FAILED = api.VM_CREATE_FAILED + VM_CREATE_DISK = api.VM_CREATE_DISK + VM_DISK_FAILED = api.VM_DISK_FAILED + VM_START_DEPLOY = api.VM_START_DEPLOY + VM_DEPLOYING = api.VM_DEPLOYING + VM_DEPLOY_FAILED = api.VM_DEPLOY_FAILED + VM_READY = api.VM_READY + VM_START_START = api.VM_START_START + VM_STARTING = api.VM_STARTING + VM_START_FAILED = api.VM_START_FAILED // # = ready + VM_RUNNING = api.VM_RUNNING + VM_START_STOP = api.VM_START_STOP + VM_STOPPING = api.VM_STOPPING + VM_STOP_FAILED = api.VM_STOP_FAILED // # = running - VM_BACKUP_STARTING = "backup_starting" - VM_BACKUP_CREATING = "backup_creating" - VM_BACKUP_CREATE_FAILED = "backup_create_fail" - VM_DEPLOYING_BACKUP = "deploying_backup" - VM_DEPLOYING_BACKUP_FAILED = "deploging_backup_fail" - VM_DELETING_BACKUP = "deleting_backup" - VM_BACKUP_DELETE_FAILED = "backup_delete_fail" - VM_SWITCH_TO_BACKUP = "switch_to_backup" - VM_SWITCH_TO_BACKUP_FAILED = "switch_to_backup_fail" + VM_BACKUP_STARTING = api.VM_BACKUP_STARTING + VM_BACKUP_CREATING = api.VM_BACKUP_CREATING + VM_BACKUP_CREATE_FAILED = api.VM_BACKUP_CREATE_FAILED + VM_DEPLOYING_BACKUP = api.VM_DEPLOYING_BACKUP + VM_DEPLOYING_BACKUP_FAILED = api.VM_DEPLOYING_BACKUP_FAILED + VM_DELETING_BACKUP = api.VM_DELETING_BACKUP + VM_BACKUP_DELETE_FAILED = api.VM_BACKUP_DELETE_FAILED + VM_SWITCH_TO_BACKUP = api.VM_SWITCH_TO_BACKUP + VM_SWITCH_TO_BACKUP_FAILED = api.VM_SWITCH_TO_BACKUP_FAILED - VM_ATTACH_DISK_FAILED = "attach_disk_fail" - VM_DETACH_DISK_FAILED = "detach_disk_fail" + VM_ATTACH_DISK_FAILED = api.VM_ATTACH_DISK_FAILED + VM_DETACH_DISK_FAILED = api.VM_DETACH_DISK_FAILED - VM_START_SUSPEND = "start_suspend" - VM_SUSPENDING = "suspending" - VM_SUSPEND = "suspend" - VM_SUSPEND_FAILED = "suspend_failed" + VM_START_SUSPEND = api.VM_START_SUSPEND + VM_SUSPENDING = api.VM_SUSPENDING + VM_SUSPEND = api.VM_SUSPEND + VM_SUSPEND_FAILED = api.VM_SUSPEND_FAILED - VM_START_DELETE = "start_delete" - VM_DELETE_FAIL = "delete_fail" - VM_DELETING = "deleting" + VM_START_DELETE = api.VM_START_DELETE + VM_DELETE_FAIL = api.VM_DELETE_FAIL + VM_DELETING = api.VM_DELETING - VM_DEALLOCATED = "deallocated" + VM_DEALLOCATED = api.VM_DEALLOCATED - VM_START_MIGRATE = "start_migrate" - VM_MIGRATING = "migrating" - VM_MIGRATE_FAILED = "migrate_failed" + VM_START_MIGRATE = api.VM_START_MIGRATE + VM_MIGRATING = api.VM_MIGRATING + VM_MIGRATE_FAILED = api.VM_MIGRATE_FAILED - VM_CHANGE_FLAVOR = "change_flavor" - VM_CHANGE_FLAVOR_FAIL = "change_flavor_fail" - VM_REBUILD_ROOT = "rebuild_root" - VM_REBUILD_ROOT_FAIL = "rebuild_root_fail" + VM_CHANGE_FLAVOR = api.VM_CHANGE_FLAVOR + VM_CHANGE_FLAVOR_FAIL = api.VM_CHANGE_FLAVOR_FAIL + VM_REBUILD_ROOT = api.VM_REBUILD_ROOT + VM_REBUILD_ROOT_FAIL = api.VM_REBUILD_ROOT_FAIL - VM_START_SNAPSHOT = "snapshot_start" - VM_SNAPSHOT = "snapshot" - VM_SNAPSHOT_DELETE = "snapshot_delete" - VM_BLOCK_STREAM = "block_stream" - VM_MIRROR_FAIL = "mirror_failed" - VM_SNAPSHOT_SUCC = "snapshot_succ" - VM_SNAPSHOT_FAILED = "snapshot_failed" + VM_START_SNAPSHOT = api.VM_START_SNAPSHOT + VM_SNAPSHOT = api.VM_SNAPSHOT + VM_SNAPSHOT_DELETE = api.VM_SNAPSHOT_DELETE + VM_BLOCK_STREAM = api.VM_BLOCK_STREAM + VM_MIRROR_FAIL = api.VM_MIRROR_FAIL + VM_SNAPSHOT_SUCC = api.VM_SNAPSHOT_SUCC + VM_SNAPSHOT_FAILED = api.VM_SNAPSHOT_FAILED - VM_SYNCING_STATUS = "syncing" - VM_SYNC_CONFIG = "sync_config" - VM_SYNC_FAIL = "sync_fail" + VM_SYNCING_STATUS = api.VM_SYNCING_STATUS + VM_SYNC_CONFIG = api.VM_SYNC_CONFIG + VM_SYNC_FAIL = api.VM_SYNC_FAIL - VM_RESIZE_DISK = "resize_disk" - VM_RESIZE_DISK_FAILED = "resize_disk_fail" - VM_START_SAVE_DISK = "start_save_disk" - VM_SAVE_DISK = "save_disk" - VM_SAVE_DISK_FAILED = "save_disk_failed" + VM_RESIZE_DISK = api.VM_RESIZE_DISK + VM_RESIZE_DISK_FAILED = api.VM_RESIZE_DISK_FAILED + VM_START_SAVE_DISK = api.VM_START_SAVE_DISK + VM_SAVE_DISK = api.VM_SAVE_DISK + VM_SAVE_DISK_FAILED = api.VM_SAVE_DISK_FAILED - VM_RESTORING_SNAPSHOT = "restoring_snapshot" - VM_RESTORE_DISK = "restore_disk" - VM_RESTORE_STATE = "restore_state" - VM_RESTORE_FAILED = "restore_failed" + VM_RESTORING_SNAPSHOT = api.VM_RESTORING_SNAPSHOT + VM_RESTORE_DISK = api.VM_RESTORE_DISK + VM_RESTORE_STATE = api.VM_RESTORE_STATE + VM_RESTORE_FAILED = api.VM_RESTORE_FAILED - VM_ASSOCIATE_EIP = "associate_eip" - VM_ASSOCIATE_EIP_FAILED = "associate_eip_failed" - VM_DISSOCIATE_EIP = "dissociate_eip" - VM_DISSOCIATE_EIP_FAILED = "dissociate_eip_failed" + VM_ASSOCIATE_EIP = api.VM_ASSOCIATE_EIP + VM_ASSOCIATE_EIP_FAILED = api.VM_ASSOCIATE_EIP_FAILED + VM_DISSOCIATE_EIP = api.VM_DISSOCIATE_EIP + VM_DISSOCIATE_EIP_FAILED = api.VM_DISSOCIATE_EIP_FAILED - VM_REMOVE_STATEFILE = "remove_state" + VM_REMOVE_STATEFILE = api.VM_REMOVE_STATEFILE - VM_ADMIN = "admin" + VM_ADMIN = api.VM_ADMIN - SHUTDOWN_STOP = "stop" - SHUTDOWN_TERMINATE = "terminate" + SHUTDOWN_STOP = api.SHUTDOWN_STOP + SHUTDOWN_TERMINATE = api.SHUTDOWN_TERMINATE - HYPERVISOR_KVM = "kvm" - HYPERVISOR_CONTAINER = "container" - HYPERVISOR_BAREMETAL = "baremetal" - HYPERVISOR_ESXI = "esxi" - HYPERVISOR_HYPERV = "hyperv" - HYPERVISOR_XEN = "xen" + HYPERVISOR_KVM = api.HYPERVISOR_KVM + HYPERVISOR_CONTAINER = api.HYPERVISOR_CONTAINER + HYPERVISOR_BAREMETAL = api.HYPERVISOR_BAREMETAL + HYPERVISOR_ESXI = api.HYPERVISOR_ESXI + HYPERVISOR_HYPERV = api.HYPERVISOR_HYPERV + HYPERVISOR_XEN = api.HYPERVISOR_XEN - HYPERVISOR_ALIYUN = "aliyun" - HYPERVISOR_QCLOUD = "qcloud" - HYPERVISOR_AZURE = "azure" - HYPERVISOR_AWS = "aws" - HYPERVISOR_HUAWEI = "huawei" - HYPERVISOR_OPENSTACK = "openstack" + HYPERVISOR_ALIYUN = api.HYPERVISOR_ALIYUN + HYPERVISOR_QCLOUD = api.HYPERVISOR_QCLOUD + HYPERVISOR_AZURE = api.HYPERVISOR_AZURE + HYPERVISOR_AWS = api.HYPERVISOR_AWS + HYPERVISOR_HUAWEI = api.HYPERVISOR_HUAWEI + HYPERVISOR_OPENSTACK = api.HYPERVISOR_OPENSTACK // HYPERVISOR_DEFAULT = HYPERVISOR_KVM HYPERVISOR_DEFAULT = HYPERVISOR_KVM ) -var VM_RUNNING_STATUS = []string{VM_START_START, VM_STARTING, VM_RUNNING, VM_BLOCK_STREAM} -var VM_CREATING_STATUS = []string{VM_CREATE_NETWORK, VM_CREATE_DISK, VM_START_DEPLOY, VM_DEPLOYING} +var VM_RUNNING_STATUS = api.VM_RUNNING_STATUS +var VM_CREATING_STATUS = api.VM_CREATING_STATUS -var HYPERVISORS = []string{HYPERVISOR_KVM, - HYPERVISOR_BAREMETAL, - HYPERVISOR_ESXI, - HYPERVISOR_CONTAINER, - HYPERVISOR_ALIYUN, - HYPERVISOR_AZURE, - HYPERVISOR_AWS, - HYPERVISOR_QCLOUD, - HYPERVISOR_HUAWEI, - HYPERVISOR_OPENSTACK, -} +var HYPERVISORS = api.HYPERVISORS -var PUBLIC_CLOUD_HYPERVISORS = []string{ - HYPERVISOR_ALIYUN, - HYPERVISOR_AWS, - HYPERVISOR_AZURE, - HYPERVISOR_QCLOUD, - HYPERVISOR_HUAWEI, - HYPERVISOR_OPENSTACK, -} +var PUBLIC_CLOUD_HYPERVISORS = api.PUBLIC_CLOUD_HYPERVISORS // var HYPERVISORS = []string{HYPERVISOR_ALIYUN} -var HYPERVISOR_HOSTTYPE = map[string]string{ - HYPERVISOR_KVM: HOST_TYPE_HYPERVISOR, - HYPERVISOR_BAREMETAL: HOST_TYPE_BAREMETAL, - HYPERVISOR_ESXI: HOST_TYPE_ESXI, - HYPERVISOR_CONTAINER: HOST_TYPE_KUBELET, - HYPERVISOR_ALIYUN: HOST_TYPE_ALIYUN, - HYPERVISOR_AZURE: HOST_TYPE_AZURE, - HYPERVISOR_AWS: HOST_TYPE_AWS, - HYPERVISOR_QCLOUD: HOST_TYPE_QCLOUD, - HYPERVISOR_HUAWEI: HOST_TYPE_HUAWEI, - HYPERVISOR_OPENSTACK: HOST_TYPE_OPENSTACK, -} +var HYPERVISOR_HOSTTYPE = api.HYPERVISOR_HOSTTYPE -var HOSTTYPE_HYPERVISOR = map[string]string{ - HOST_TYPE_HYPERVISOR: HYPERVISOR_KVM, - HOST_TYPE_BAREMETAL: HYPERVISOR_BAREMETAL, - HOST_TYPE_ESXI: HYPERVISOR_ESXI, - HOST_TYPE_KUBELET: HYPERVISOR_CONTAINER, - HOST_TYPE_ALIYUN: HYPERVISOR_ALIYUN, - HOST_TYPE_AZURE: HYPERVISOR_AZURE, - HOST_TYPE_AWS: HYPERVISOR_AWS, - HOST_TYPE_QCLOUD: HYPERVISOR_QCLOUD, - HOST_TYPE_HUAWEI: HYPERVISOR_HUAWEI, - HOST_TYPE_OPENSTACK: HYPERVISOR_OPENSTACK, -} +var HOSTTYPE_HYPERVISOR = api.HOSTTYPE_HYPERVISOR type SGuestManager struct { db.SVirtualResourceBaseManager @@ -747,32 +708,61 @@ func (guest *SGuest) ValidateResizeDisk(disk *SDisk, storage *SStorage) error { return guest.GetDriver().ValidateResizeDisk(guest, disk, storage) } +func ValidateMemData(vmemSize int, driver IGuestDriver) (int, error) { + if vmemSize > 0 { + maxVmemGb := driver.GetMaxVMemSizeGB() + if vmemSize < 8 || vmemSize > maxVmemGb*1024 { + return 0, httperrors.NewInputParameterError("Memory size must be 8MB ~ %d GB", maxVmemGb) + } + } + return vmemSize, nil +} + +func ValidateCpuData(vcpuCount int, driver IGuestDriver) (int, error) { + maxVcpuCount := driver.GetMaxVCpuCount() + if vcpuCount < 1 || vcpuCount > maxVcpuCount { + return 0, httperrors.NewInputParameterError("CPU core count must be 1 ~ %d", maxVcpuCount) + } + return vcpuCount, nil +} + func ValidateMemCpuData(vmemSize, vcpuCount int, hypervisor string) (int, int, error) { if len(hypervisor) == 0 { hypervisor = HYPERVISOR_DEFAULT } driver := GetDriver(hypervisor) - if vmemSize > 0 { - maxVmemGb := driver.GetMaxVMemSizeGB() - if vmemSize < 8 || vmemSize > maxVmemGb*1024 { - return 0, 0, httperrors.NewInputParameterError("Memory size must be 8MB ~ %d GB", maxVmemGb) - } + var err error + vmemSize, err = ValidateMemData(vmemSize, driver) + if err != nil { + return 0, 0, err } - maxVcpuCount := driver.GetMaxVCpuCount() - if vcpuCount < 1 || vcpuCount > maxVcpuCount { - return 0, 0, httperrors.NewInputParameterError("CPU core count must be 1 ~ %d", maxVcpuCount) + vcpuCount, err = ValidateCpuData(vcpuCount, driver) + if err != nil { + return 0, 0, err } return vmemSize, vcpuCount, nil } func (self *SGuest) ValidateUpdateData(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, data *jsonutils.JSONDict) (*jsonutils.JSONDict, error) { - memSize, _ := data.Int("vmem_size") - cpuCount, _ := data.Int("vcpu_count") - vmemSize, vcpuCount, err := ValidateMemCpuData(int(memSize), int(cpuCount), self.Hypervisor) - if err != nil { - return nil, err + var err error + var vmemSize int + var vcpuCount int + + driver := GetDriver(self.Hypervisor) + + if memSize, _ := data.Int("vmem_size"); memSize != 0 { + vmemSize, err = ValidateMemData(int(memSize), driver) + if err != nil { + return nil, err + } + } + if cpuCount, _ := data.Int("vcpu_count"); cpuCount != 0 { + vcpuCount, err = ValidateCpuData(int(cpuCount), driver) + if err != nil { + return nil, err + } } if vmemSize > 0 || vcpuCount > 0 { @@ -2726,8 +2716,7 @@ func (self *SGuest) createDiskOnHost( } func (self *SGuest) CreateIsolatedDeviceOnHost(ctx context.Context, userCred mcclient.TokenCredential, host *SHost, devs []*api.IsolatedDeviceConfig, pendingUsage quotas.IQuota) error { - for idx := 0; idx < len(devs); idx += 1 { - devConfig := devs[idx] + for _, devConfig := range devs { err := self.createIsolatedDeviceOnHost(ctx, userCred, host, devConfig, pendingUsage) if err != nil { return err diff --git a/pkg/compute/models/isolated_devices.go b/pkg/compute/models/isolated_devices.go index c8358842d7..da17dceb94 100644 --- a/pkg/compute/models/isolated_devices.go +++ b/pkg/compute/models/isolated_devices.go @@ -357,7 +357,7 @@ func (manager *SIsolatedDeviceManager) findHostUnusedByModel(model string, hostI devs := make([]SIsolatedDevice, 0) q := manager.findUnusedQuery() q = q.Equals("model", model).Equals("host_id", hostId) - err := q.All(&devs) + err := db.FetchModelObjects(manager, q, &devs) if err != nil { return nil, err } @@ -444,7 +444,6 @@ func (self *SIsolatedDevice) GetSpec(statusCheck bool) *jsonutils.JSONDict { } func (man *SIsolatedDeviceManager) GetSpecIdent(spec *jsonutils.JSONDict) []string { - log.Errorf("gpu get specIdent: %s", spec) devType, _ := spec.GetString("dev_type") vendor, _ := spec.GetString("vendor") model, _ := spec.GetString("model") diff --git a/pkg/compute/models/networks.go b/pkg/compute/models/networks.go index 1236484d48..c5bc1406f3 100644 --- a/pkg/compute/models/networks.go +++ b/pkg/compute/models/networks.go @@ -705,19 +705,21 @@ type SNicConfig struct { } func parseNetworkInfo(userCred mcclient.TokenCredential, info *api.NetworkConfig) (*api.NetworkConfig, error) { - netObj, err := NetworkManager.FetchByIdOrName(userCred, info.Network) - if err != nil { - if err == sql.ErrNoRows { - return nil, httperrors.NewResourceNotFoundError2(NetworkManager.Keyword(), info.Network) + if info.Network != "" { + netObj, err := NetworkManager.FetchByIdOrName(userCred, info.Network) + if err != nil { + if err == sql.ErrNoRows { + return nil, httperrors.NewResourceNotFoundError2(NetworkManager.Keyword(), info.Network) + } else { + return nil, err + } + } + net := netObj.(*SNetwork) + if net.IsOwner(userCred) || net.IsPublic || db.IsAdminAllowGet(userCred, net) { + info.Network = netObj.GetId() } else { - return nil, err + return nil, httperrors.NewForbiddenError("no allow to access network %s", info.Network) } - } - net := netObj.(*SNetwork) - if net.IsOwner(userCred) || net.IsPublic || db.IsAdminAllowGet(userCred, net) { - info.Network = netObj.GetId() - } else { - return nil, httperrors.NewForbiddenError("no allow to access network %s", info.Network) } if info.BwLimit == 0 { diff --git a/pkg/hostman/guestman/qemu-kvmhelper.go b/pkg/hostman/guestman/qemu-kvmhelper.go index 4f187c06b5..67dccfed97 100644 --- a/pkg/hostman/guestman/qemu-kvmhelper.go +++ b/pkg/hostman/guestman/qemu-kvmhelper.go @@ -291,12 +291,10 @@ func (s *SKVMGuestInstance) generateStartScript(data *jsonutils.JSONDict) (strin } var devAddrs = []string{} - isolatedParams, err := s.Desc.GetArray("isolated_devices") - if err != nil { - for _, params := range isolatedParams { - devAddr, _ := params.GetString("addr") - devAddrs = append(devAddrs, devAddr) - } + isolatedParams, _ := s.Desc.GetArray("isolated_devices") + for _, params := range isolatedParams { + devAddr, _ := params.GetString("addr") + devAddrs = append(devAddrs, devAddr) } isolatedDevsParams := s.manager.GetHost().GetIsolatedDeviceManager().GetQemuParams(devAddrs) diff --git a/pkg/hostman/isolated_device/isolated_device.go b/pkg/hostman/isolated_device/isolated_device.go index 0bf974df2b..70a9acf55f 100644 --- a/pkg/hostman/isolated_device/isolated_device.go +++ b/pkg/hostman/isolated_device/isolated_device.go @@ -104,7 +104,7 @@ func (man *IsolatedDeviceManager) fillPCIDevices() error { } for idx, gpu := range gpus { man.Devices = append(man.Devices, newGPUHPCDevice(gpu)) - log.Infof("GPU device: %d => %#v", idx, gpu) + log.Infof("Add GPU device: %d => %#v", idx, gpu) } return nil } @@ -328,6 +328,7 @@ func (dev *sGPUBaseDevice) CustomProbe() error { if err != nil { return err } + grubCmdline = strings.TrimSpace(grubCmdline) params := sets.NewString(strings.Split(grubCmdline, " ")...) if !params.IsSuperset(sets.NewString("intel_iommu=on", "vfio_iommu_type1.allow_unsafe_interrupts=1")) { @@ -614,7 +615,7 @@ func (d *PCIDevice) getKernelDriver() (string, error) { begin := strings.Index(line, prompt) if begin >= 0 { end := begin + len(prompt) - return line[end : len(line)-1], nil + return line[end:], nil } } // no driver in use @@ -729,6 +730,8 @@ func getPassthroughGPUS() ([]*PCIDevice, error) { log.Errorf("Device %#v get kernel driver error: %v", dev, err) } else if drv == VFIO_PCI_KERNEL_DRIVER { ret = append(ret, dev) + } else { + log.Warningf("GPU %v use kernel driver %q, skip it", dev, drv) } } return ret, nil diff --git a/pkg/mcclient/options/schedulers.go b/pkg/mcclient/options/schedulers.go index f22a37f18c..d4f3785b44 100644 --- a/pkg/mcclient/options/schedulers.go +++ b/pkg/mcclient/options/schedulers.go @@ -6,7 +6,7 @@ import ( ) type SchedulerTestBaseOptions struct { - *ServerConfigs + ServerConfigs Mem int `help:"Memory size (MB), default 512" metavar:"MEMORY" default:"512"` Ncpu int `help:"#CPU cores of VM server, default 1" default:"1" metavar:""` diff --git a/pkg/mcclient/options/servers.go b/pkg/mcclient/options/servers.go index 2e0e9a3934..8c1277947a 100644 --- a/pkg/mcclient/options/servers.go +++ b/pkg/mcclient/options/servers.go @@ -347,7 +347,8 @@ func (opts *ServerCreateOptions) Params() (*computeapi.ServerCreateInput, error) } if BoolV(opts.AllowDelete) { - params.DisableDelete = false + disableDelete := false + params.DisableDelete = &disableDelete } if BoolV(opts.DryRun) { diff --git a/pkg/scheduler/cache/candidate/hosts.go b/pkg/scheduler/cache/candidate/hosts.go index 0cad7c9ce1..a77fe42764 100644 --- a/pkg/scheduler/cache/candidate/hosts.go +++ b/pkg/scheduler/cache/candidate/hosts.go @@ -15,6 +15,7 @@ import ( "yunion.io/x/pkg/utils" "yunion.io/x/sqlchemy" + api "yunion.io/x/onecloud/pkg/apis/compute" computedb "yunion.io/x/onecloud/pkg/cloudcommon/db" computemodels "yunion.io/x/onecloud/pkg/compute/models" "yunion.io/x/onecloud/pkg/scheduler/core" @@ -121,18 +122,24 @@ func NewGuestReservedResourceUsedByBuilder(b *HostBuilder, host *computemodels.S mem int64 = 0 disk int64 = 0 ) - for _, g := range gst { - dSize, err2 := g.DiskSize(true) - if err2 != nil { - err = err2 - return + guestDiskSize := func(g *computemodels.SGuest, onlyLocal bool) int { + size := 0 + for _, gd := range g.GetDisks() { + disk := gd.GetDisk() + if !onlyLocal || disk.IsLocal() { + size += disk.DiskSize + } } - disk += dSize - if o.GetOptions().IgnoreNonrunningGuests && !g.IsRunning() { + return size + } + for _, g := range gst { + dSize := guestDiskSize(&g, true) + disk += int64(dSize) + if o.GetOptions().IgnoreNonrunningGuests && !utils.IsInStringArray(g.Status, api.VM_RUNNING_STATUS) { continue } - cpu += g.VCPUCount - mem += g.VMemSize + cpu += int64(g.VcpuCount) + mem += int64(g.VmemSize) } ret.CPUCount = cpu ret.MemorySize = mem @@ -464,8 +471,9 @@ func (h *HostDesc) UnusedIsolatedDevicesByType(devType string) []*IsolatedDevice func (h *HostDesc) UnusedIsolatedDevicesByVendorModel(vendorModel string) []*IsolatedDeviceDesc { ret := make([]*IsolatedDeviceDesc, 0) + vm := NewVendorModelByStr(vendorModel) for _, dev := range h.UnusedIsolatedDevices() { - if strings.Contains(dev.VendorModel(), vendorModel) { + if dev.GetVendorModel().IsMatch(vm) { ret = append(ret, dev) } } @@ -1192,8 +1200,50 @@ func (i *IsolatedDeviceDesc) VendorID() string { return strings.Split(i.VendorDeviceID, ":")[0] } -func (i *IsolatedDeviceDesc) VendorModel() string { - return fmt.Sprintf("%s:%s", i.VendorID(), i.Model) +type VendorModel struct { + Vendor string + Model string +} + +func NewVendorModelByStr(desc string) *VendorModel { + vm := new(VendorModel) + // desc format is ':' + parts := strings.Split(desc, ":") + if len(parts) == 1 { + vm.Model = parts[0] + } else if len(parts) == 2 { + vm.Vendor = parts[0] + vm.Model = parts[1] + } + return vm +} + +func (vm *VendorModel) IsMatch(target *VendorModel) bool { + if vm.Model == "" || target.Model == "" { + return false + } + vendorMatch := false + modelMatch := false + if target.Vendor != "" { + if vm.Vendor == target.Vendor { + vendorMatch = true + } else if api.ID_VENDOR_MAP[vm.Vendor] == target.Vendor { + vendorMatch = true + } + } else { + vendorMatch = true + } + if vm.Model == target.Model { + modelMatch = true + } + return vendorMatch && modelMatch +} + +func (i *IsolatedDeviceDesc) GetVendorModel() *VendorModel { + return &VendorModel{ + Vendor: i.VendorID(), + Model: i.Model, + } } func (b *HostBuilder) getIsolatedDevices(hostID string) (devs []*models.IsolatedDevice) { @@ -1219,8 +1269,8 @@ func (b *HostBuilder) getUsedIsolatedDevices(hostID string) (devs []*models.Isol return } -func (b *HostBuilder) getIsolatedDeviceGuests(hostID string) (guests []*models.Guest) { - guests = make([]*models.Guest, 0) +func (b *HostBuilder) getIsolatedDeviceGuests(hostID string) (guests []computemodels.SGuest) { + guests = make([]computemodels.SGuest, 0) usedDevs := b.getUsedIsolatedDevices(hostID) if len(usedDevs) == 0 { return @@ -1231,9 +1281,9 @@ func (b *HostBuilder) getIsolatedDeviceGuests(hostID string) (guests []*models.G if !ok { continue } - guest := g.(*models.Guest) - if !ids.Has(guest.ID) { - ids.Insert(guest.ID) + guest := g.(computemodels.SGuest) + if !ids.Has(guest.Id) { + ids.Insert(guest.Id) guests = append(guests, guest) } } From 0a45a1d8a176b0db04abf989086a37d8ec3525b7 Mon Sep 17 00:00:00 2001 From: wanyaoqi Date: Wed, 20 Mar 2019 14:43:43 +0800 Subject: [PATCH 06/10] fix live migrate set status --- pkg/compute/tasks/guest_live_migrate_task.go | 18 +++++------------- 1 file changed, 5 insertions(+), 13 deletions(-) diff --git a/pkg/compute/tasks/guest_live_migrate_task.go b/pkg/compute/tasks/guest_live_migrate_task.go index 2d2b88d2e5..6abeaf696a 100644 --- a/pkg/compute/tasks/guest_live_migrate_task.go +++ b/pkg/compute/tasks/guest_live_migrate_task.go @@ -354,26 +354,18 @@ func (self *GuestLiveMigrateTask) OnResumeDestGuestComplete(ctx context.Context, func (self *GuestLiveMigrateTask) OnUndeploySrcGuestComplete(ctx context.Context, guest *models.SGuest, data jsonutils.JSONObject) { db.OpsLog.LogEvent(guest, db.ACT_MIGRATE, "", self.UserCred) status, _ := self.Params.GetString("guest_status") - if status != models.VM_RUNNING { - guest.SetStatus(self.UserCred, status, "") - self.SetStageComplete(ctx, nil) - } else { - self.SetStage("OnStartGeustComplete", nil) - guest.StartGueststartTask(ctx, self.UserCred, nil, self.GetTaskId()) + if status != guest.Status { + self.SetStage("OnGuestSyncStatus", nil) + guest.StartSyncstatus(ctx, self.UserCred, self.GetTaskId()) } } -func (self *GuestLiveMigrateTask) OnStartGeustComplete(ctx context.Context, guest *models.SGuest, data jsonutils.JSONObject) { +func (self *GuestLiveMigrateTask) OnGuestSyncStatus(ctx context.Context, guest *models.SGuest, data jsonutils.JSONObject) { self.SetStageComplete(ctx, nil) } func (self *GuestMigrateTask) TaskFailed(ctx context.Context, guest *models.SGuest, reason string) { - status, _ := self.Params.GetString("guest_status") - if status != models.VM_RUNNING { - guest.SetStatus(self.UserCred, status, "") - } else { - guest.StartGueststartTask(ctx, self.UserCred, nil, "") - } + guest.SetStatus(self.UserCred, models.VM_MIGRATE_FAILED, reason) db.OpsLog.LogEvent(guest, db.ACT_MIGRATE_FAIL, reason, self.UserCred) self.SetStageFailed(ctx, reason) notifyclient.NotifySystemError(guest.Id, guest.Name, models.VM_MIGRATE_FAILED, reason) From 06ae94ebe3e434a8148b5103c9fc5b9dcfd93674 Mon Sep 17 00:00:00 2001 From: Qiu Jian Date: Thu, 21 Mar 2019 01:03:51 +0800 Subject: [PATCH 07/10] fix: simplify rbac policy condition to match projects and roles --- cmd/climc/shell/policies.go | 7 +- pkg/cloudcommon/policy/policy.go | 34 ++++--- pkg/mcclient/modules/mod_policies.go | 10 ++ pkg/util/rbacutils/rabc.go | 94 +++++++++++++++--- pkg/util/rbacutils/rabc_test.go | 143 ++++++++++++++------------- 5 files changed, 193 insertions(+), 95 deletions(-) diff --git a/cmd/climc/shell/policies.go b/cmd/climc/shell/policies.go index e468e7f3c6..bf3f4fc574 100644 --- a/cmd/climc/shell/policies.go +++ b/cmd/climc/shell/policies.go @@ -253,7 +253,7 @@ func init() { } req.Add(jsonutils.NewArray(data...), key) } - fmt.Println(req.String()) + fmt.Println("Request:", req.String()) var token mcclient.TokenCredential if len(args.User) > 0 { @@ -273,6 +273,11 @@ func init() { } printObject(result) + fmt.Println("userCred:", token) + m := policy.PolicyManager.MatchedPolicies(false, token) + fmt.Println("matched policies:", m) + m = policy.PolicyManager.MatchedPolicies(true, token) + fmt.Println("matched admin policies:", m) return nil }) } diff --git a/pkg/cloudcommon/policy/policy.go b/pkg/cloudcommon/policy/policy.go index 86bb0dcef2..f2ac40ea1c 100644 --- a/pkg/cloudcommon/policy/policy.go +++ b/pkg/cloudcommon/policy/policy.go @@ -16,7 +16,7 @@ import ( "yunion.io/x/onecloud/pkg/mcclient" "yunion.io/x/onecloud/pkg/mcclient/auth" "yunion.io/x/onecloud/pkg/mcclient/modules" - "yunion.io/x/onecloud/pkg/util/conditionparser" + // "yunion.io/x/onecloud/pkg/util/conditionparser" "yunion.io/x/onecloud/pkg/util/hashcache" "yunion.io/x/onecloud/pkg/util/rbacutils" ) @@ -231,27 +231,21 @@ func (manager *SPolicyManager) allowWithoutCache(isAdmin bool, userCred mcclient log.Warningf("no policies fetched") return rbacutils.Deny } - var userCredJson jsonutils.JSONObject - if userCred != nil { - userCredJson = userCred.ToJson() - } else { - userCredJson = jsonutils.NewDict() - } currentPriv := rbacutils.Deny for _, p := range policies { - result := p.Allow(userCredJson, service, resource, action, extra...) + result := p.Allow(userCred, service, resource, action, extra...) if currentPriv.StricterThan(result) { currentPriv = result } } if !isAdmin && manager.defaultPolicy != nil { - result := manager.defaultPolicy.Allow(userCredJson, service, resource, action, extra...) + result := manager.defaultPolicy.Allow(userCred, service, resource, action, extra...) if currentPriv.StricterThan(result) { currentPriv = result } } if consts.IsRbacDebug() { - log.Debugf("[RBAC: %v] %s %s %s %#v permission %s userCred: %s", isAdmin, service, resource, action, extra, currentPriv, userCredJson) + log.Debugf("[RBAC: %v] %s %s %s %#v permission %s userCred: %s", isAdmin, service, resource, action, extra, currentPriv, userCred) } return unifyRbacResult(isAdmin, currentPriv) } @@ -365,12 +359,26 @@ func (manager *SPolicyManager) IsAdminCapable(userCred mcclient.TokenCredential) return true } - userCredJson := userCred.ToJson() for _, p := range manager.adminPolicies { - match, _ := conditionparser.Eval(p.Condition, userCredJson) - if match { + if p.Match(userCred) { return true } } return false } + +func (manager *SPolicyManager) MatchedPolicies(isAdmin bool, userCred mcclient.TokenCredential) []string { + var policies map[string]rbacutils.SRbacPolicy + if isAdmin { + policies = manager.adminPolicies + } else { + policies = manager.policies + } + ret := make([]string, 0) + for k, p := range policies { + if p.Match(userCred) { + ret = append(ret, k) + } + } + return ret +} diff --git a/pkg/mcclient/modules/mod_policies.go b/pkg/mcclient/modules/mod_policies.go index 0e0e81f9d0..ce0f125dcf 100644 --- a/pkg/mcclient/modules/mod_policies.go +++ b/pkg/mcclient/modules/mod_policies.go @@ -4,6 +4,7 @@ import ( "yunion.io/x/jsonutils" "yunion.io/x/onecloud/pkg/mcclient" + "yunion.io/x/onecloud/pkg/util/rbacutils" ) type SPolicyManager struct { @@ -17,7 +18,16 @@ func policyReadFilter(session *mcclient.ClientSession, s jsonutils.JSONObject, q ret := ss.CopyIncludes("id", "type") blobStr, _ := ss.GetString("blob") if len(blobStr) > 0 { + policy := rbacutils.SRbacPolicy{} blobJson, _ := jsonutils.ParseString(blobStr) + err := policy.Decode(blobJson) + if err != nil { + return nil, err + } + blobJson, err = policy.Encode() + if err != nil { + return nil, err + } var format string if query != nil { format, _ = query.GetString("format") diff --git a/pkg/util/rbacutils/rabc.go b/pkg/util/rbacutils/rabc.go index 3c11d60d45..7ef001dd65 100644 --- a/pkg/util/rbacutils/rabc.go +++ b/pkg/util/rbacutils/rabc.go @@ -2,11 +2,11 @@ package rbacutils import ( "fmt" + "regexp" "yunion.io/x/jsonutils" - "yunion.io/x/log" - "yunion.io/x/onecloud/pkg/util/conditionparser" + "yunion.io/x/onecloud/pkg/mcclient" ) type TRbacResult string @@ -45,6 +45,8 @@ type SRbacPolicy struct { Condition string IsAdmin bool Rules []SRbacRule + Projects []string + Roles []string } type SRbacRule struct { @@ -188,9 +190,48 @@ func CompactRules(rules []SRbacRule) []SRbacRule { return output } +var ( + tenantEqualsPattern = regexp.MustCompile(`tenant\s*==\s*['"]?(\w+)['"]?`) + roleContainsPattern = regexp.MustCompile(`roles.contains\(['"]?(\w+)['"]?\)`) +) + +func searchMatchStrings(pattern *regexp.Regexp, condstr string) []string { + ret := make([]string, 0) + matches := pattern.FindAllStringSubmatch(condstr, -1) + for _, match := range matches { + ret = append(ret, match[1]) + } + return ret +} + +func searchMatchTenants(condstr string) []string { + return searchMatchStrings(tenantEqualsPattern, condstr) +} + +func searchMatchRoles(condstr string) []string { + return searchMatchStrings(roleContainsPattern, condstr) +} + func (policy *SRbacPolicy) Decode(policyJson jsonutils.JSONObject) error { policy.Condition, _ = policyJson.GetString("condition") policy.IsAdmin = jsonutils.QueryBoolean(policyJson, "is_admin", false) + if policyJson.Contains("projects") { + projectJson, _ := policyJson.GetArray("projects") + policy.Projects = jsonutils.JSONArray2StringArray(projectJson) + } + if policyJson.Contains("roles") { + roleJson, _ := policyJson.GetArray("roles") + policy.Roles = jsonutils.JSONArray2StringArray(roleJson) + } + + if len(policy.Projects) == 0 && len(policy.Roles) == 0 && len(policy.Condition) > 0 { + // XXX hack + // for smooth transtion from condition to projects&roles + policy.Projects = searchMatchTenants(policy.Condition) + policy.Roles = searchMatchRoles(policy.Condition) + } + // empty condition, no longer use this field + policy.Condition = "" ruleJson, err := policyJson.Get("policy") if err != nil { @@ -288,7 +329,7 @@ func (rule *SRbacRule) toStringArray() []string { strArr = append(strArr, rule.Extra...) } i := len(strArr) - 1 - for i >= 0 && (len(strArr[i]) == 0 || strArr[i] == WILD_MATCH) { + for i > 0 && (len(strArr[i]) == 0 || strArr[i] == WILD_MATCH) { i -= 1 } return strArr[0 : i+1] @@ -346,12 +387,18 @@ func (policy *SRbacPolicy) Encode() (jsonutils.JSONObject, error) { } ret := jsonutils.NewDict() - ret.Add(jsonutils.NewString(policy.Condition), "condition") + // ret.Add(jsonutils.NewString(policy.Condition), "condition") if policy.IsAdmin { ret.Add(jsonutils.JSONTrue, "is_admin") } else { ret.Add(jsonutils.JSONFalse, "is_admin") } + if len(policy.Projects) > 0 { + ret.Add(jsonutils.NewStringArray(policy.Projects), "projects") + } + if len(policy.Roles) > 0 { + ret.Add(jsonutils.NewStringArray(policy.Roles), "roles") + } ret.Add(rules, "policy") return ret, nil } @@ -369,17 +416,40 @@ func (policy *SRbacPolicy) Explain(request [][]string) [][]string { return output } -func (policy *SRbacPolicy) Allow(userCred jsonutils.JSONObject, service, resource, action string, extra ...string) TRbacResult { - if len(policy.Condition) > 0 { - match, err := conditionparser.Eval(policy.Condition, userCred) - if err != nil { - log.Errorf("eval condition %s fail %s", policy.Condition, err) - return Deny +func contains(s1 []string, s string) bool { + for i := range s1 { + if s1[i] == s { + return true } - if !match { - return Deny + } + return false +} + +func intersect(s1 []string, s2 []string) bool { + for i := range s1 { + for j := range s2 { + if s1[i] == s2[j] { + return true + } } } + return false +} + +func (policy *SRbacPolicy) Match(userCred mcclient.TokenCredential) bool { + if len(policy.Projects) > 0 && userCred != nil && !contains(policy.Projects, userCred.GetProjectName()) { + return false + } + if len(policy.Roles) > 0 && userCred != nil && !intersect(policy.Roles, userCred.GetRoles()) { + return false + } + return true +} + +func (policy *SRbacPolicy) Allow(userCred mcclient.TokenCredential, service, resource, action string, extra ...string) TRbacResult { + if !policy.Match(userCred) { + return Deny + } rule := policy.GetMatchRule(service, resource, action, extra...) if rule == nil { return Deny diff --git a/pkg/util/rbacutils/rabc_test.go b/pkg/util/rbacutils/rabc_test.go index 56aeca8736..02c7755796 100644 --- a/pkg/util/rbacutils/rabc_test.go +++ b/pkg/util/rbacutils/rabc_test.go @@ -4,6 +4,8 @@ import ( "testing" "yunion.io/x/jsonutils" + + "yunion.io/x/onecloud/pkg/mcclient" ) func TestSRabcRule_Match(t *testing.T) { @@ -179,60 +181,6 @@ func TestSRabcPolicy_Encode(t *testing.T) { t.Logf("%s", policyStr1) } -func TestSRbacPolicy_Allow(t *testing.T) { - userCredStr := `{"domain":"Default","domain_id":"default","expires":"2018-10-28T05:33:54.000000Z","roles":"admin,teamleader","tenant":"system","tenant_id":"5d65667d112e47249ae66dbd7bc07030","token":"gAAAAABb0_jC2Qz2PpB00-pieLi4exKXq4O3QrvoqerpqoSxbp9pOLLdNWaAg0cPcd8eAjkiPhSo7VWQAVodoxnad95LdNbUf_1It8R_wXVDtO20caB7oLcas1oQt8b1cG0a7qagauP0iWVSW_dq_e92rD5Hd3SHn3Lw6ycrp_eHLskz_8EbPiI","user":"sysadmin","user_id":"dddf386b6ff24572b2e6a771d768495e"}` - - userCred, err := jsonutils.ParseString(userCredStr) - if err != nil { - t.Errorf("parse json fail %s", err) - return - } - - cases := []struct { - policy string - ops []string - want TRbacResult - }{ - { - `{ - "condition": "tenant==\"system\" && roles.contains(\"admin\")", - "is_admin": true, - "policy": { - "*": "allow" - } -}`, - []string{"compute", "servers", "list"}, - Allow, - }, - { - `{"is_admin":"false","policy":{"*":{"*":{"*":"allow","delete":"deny"}}}}`, - []string{"compute", "servers", "delete"}, - Deny, - }, - } - - for _, c := range cases { - policyJson, err := jsonutils.ParseString(c.policy) - if err != nil { - t.Errorf("fail to parse json string %s", err) - return - } - policy := SRbacPolicy{} - - err = policy.Decode(policyJson) - if err != nil { - t.Errorf("decode error %s", err) - return - } - - isAllow := policy.Allow(userCred, c.ops[0], c.ops[1], c.ops[2]) - if isAllow != c.want { - t.Errorf("%s %#v expect %#v, but get %#v", policyJson.String(), c.ops, c.want, isAllow) - return - } - } -} - func TestSRabcPolicy_Explain(t *testing.T) { policyStr := `{ "condition": "usercred.project != \"system\" && usercred.roles==\"projectowner\"", @@ -281,24 +229,81 @@ func TestSRabcPolicy_Explain(t *testing.T) { t.Logf("%#v", output) } -func TestTRbacResult_IsHigherPrivilege(t *testing.T) { - cases := []struct { - t1 TRbacResult - t2 TRbacResult - want bool - }{ - {Allow, Allow, false}, - {Allow, OwnerAllow, true}, - {Deny, Deny, false}, - {Deny, OwnerAllow, false}, - {Deny, Allow, false}, - {OwnerAllow, Allow, false}, - } +func TestConditionParser(t *testing.T) { + condition := `tenant=="system" && roles.contains("admin")` + tenants := searchMatchTenants(condition) + t.Logf("%s", tenants) + roles := searchMatchRoles(condition) + t.Logf("%s", roles) +} +func TestSRbacPolicyMatch(t *testing.T) { + cases := []struct { + policy SRbacPolicy + userCred mcclient.SSimpleToken + want bool + }{ + { + SRbacPolicy{}, + mcclient.SSimpleToken{}, + true, + }, + { + SRbacPolicy{ + Projects: []string{"system"}, + }, + mcclient.SSimpleToken{ + Project: "system", + }, + true, + }, + { + SRbacPolicy{ + Projects: []string{"system"}, + }, + mcclient.SSimpleToken{ + Project: "demo", + }, + false, + }, + { + SRbacPolicy{ + Projects: []string{"system"}, + Roles: []string{"admin"}, + }, + mcclient.SSimpleToken{ + Project: "system", + Roles: "admin", + }, + true, + }, + { + SRbacPolicy{ + Projects: []string{"system"}, + Roles: []string{"admin"}, + }, + mcclient.SSimpleToken{ + Project: "system", + Roles: "admin,_member_", + }, + true, + }, + { + SRbacPolicy{ + Projects: []string{"system"}, + Roles: []string{"admin"}, + }, + mcclient.SSimpleToken{ + Project: "system", + Roles: "_member_", + }, + false, + }, + } for _, c := range cases { - got := c.t1.IsHigherPrivilege(c.t2) + got := c.policy.Match(&c.userCred) if got != c.want { - t.Errorf("%s IsHigherPrivilege %s want %v got %v", c.t1, c.t2, c.want, got) + t.Errorf("%#v %#v got %v want %v", c.policy, c.userCred, got, c.want) } } } From 9d966e9959459730ba5786d78b25004afd2446c1 Mon Sep 17 00:00:00 2001 From: Qiu Jian Date: Thu, 21 Mar 2019 01:06:33 +0800 Subject: [PATCH 08/10] minor updates --- pkg/cloudcommon/policy/policy.go | 1 - 1 file changed, 1 deletion(-) diff --git a/pkg/cloudcommon/policy/policy.go b/pkg/cloudcommon/policy/policy.go index f2ac40ea1c..f9bed82332 100644 --- a/pkg/cloudcommon/policy/policy.go +++ b/pkg/cloudcommon/policy/policy.go @@ -16,7 +16,6 @@ import ( "yunion.io/x/onecloud/pkg/mcclient" "yunion.io/x/onecloud/pkg/mcclient/auth" "yunion.io/x/onecloud/pkg/mcclient/modules" - // "yunion.io/x/onecloud/pkg/util/conditionparser" "yunion.io/x/onecloud/pkg/util/hashcache" "yunion.io/x/onecloud/pkg/util/rbacutils" ) From cc30b585e058eae4920e96a3285ea2aa094a6f41 Mon Sep 17 00:00:00 2001 From: Qiu Jian Date: Thu, 21 Mar 2019 10:16:55 +0800 Subject: [PATCH 09/10] fix: refine policy match conditions --- pkg/util/rbacutils/rabc.go | 9 +++------ pkg/util/rbacutils/rabc_test.go | 29 +++++++++++++++++++++-------- 2 files changed, 24 insertions(+), 14 deletions(-) diff --git a/pkg/util/rbacutils/rabc.go b/pkg/util/rbacutils/rabc.go index 7ef001dd65..e95b9663e0 100644 --- a/pkg/util/rbacutils/rabc.go +++ b/pkg/util/rbacutils/rabc.go @@ -437,13 +437,10 @@ func intersect(s1 []string, s2 []string) bool { } func (policy *SRbacPolicy) Match(userCred mcclient.TokenCredential) bool { - if len(policy.Projects) > 0 && userCred != nil && !contains(policy.Projects, userCred.GetProjectName()) { - return false + if (len(policy.Projects) == 0 || (userCred != nil && contains(policy.Projects, userCred.GetProjectName()))) && (len(policy.Roles) == 0 || (userCred != nil && intersect(policy.Roles, userCred.GetRoles()))) { + return true } - if len(policy.Roles) > 0 && userCred != nil && !intersect(policy.Roles, userCred.GetRoles()) { - return false - } - return true + return false } func (policy *SRbacPolicy) Allow(userCred mcclient.TokenCredential, service, resource, action string, extra ...string) TRbacResult { diff --git a/pkg/util/rbacutils/rabc_test.go b/pkg/util/rbacutils/rabc_test.go index 02c7755796..401f6c7e1a 100644 --- a/pkg/util/rbacutils/rabc_test.go +++ b/pkg/util/rbacutils/rabc_test.go @@ -240,19 +240,24 @@ func TestConditionParser(t *testing.T) { func TestSRbacPolicyMatch(t *testing.T) { cases := []struct { policy SRbacPolicy - userCred mcclient.SSimpleToken + userCred mcclient.TokenCredential want bool }{ { SRbacPolicy{}, - mcclient.SSimpleToken{}, + &mcclient.SSimpleToken{}, + true, + }, + { + SRbacPolicy{}, + nil, true, }, { SRbacPolicy{ Projects: []string{"system"}, }, - mcclient.SSimpleToken{ + &mcclient.SSimpleToken{ Project: "system", }, true, @@ -261,7 +266,7 @@ func TestSRbacPolicyMatch(t *testing.T) { SRbacPolicy{ Projects: []string{"system"}, }, - mcclient.SSimpleToken{ + &mcclient.SSimpleToken{ Project: "demo", }, false, @@ -271,7 +276,7 @@ func TestSRbacPolicyMatch(t *testing.T) { Projects: []string{"system"}, Roles: []string{"admin"}, }, - mcclient.SSimpleToken{ + &mcclient.SSimpleToken{ Project: "system", Roles: "admin", }, @@ -282,7 +287,7 @@ func TestSRbacPolicyMatch(t *testing.T) { Projects: []string{"system"}, Roles: []string{"admin"}, }, - mcclient.SSimpleToken{ + &mcclient.SSimpleToken{ Project: "system", Roles: "admin,_member_", }, @@ -293,15 +298,23 @@ func TestSRbacPolicyMatch(t *testing.T) { Projects: []string{"system"}, Roles: []string{"admin"}, }, - mcclient.SSimpleToken{ + &mcclient.SSimpleToken{ Project: "system", Roles: "_member_", }, false, }, + { + SRbacPolicy{ + Projects: []string{"system"}, + Roles: []string{"admin"}, + }, + nil, + false, + }, } for _, c := range cases { - got := c.policy.Match(&c.userCred) + got := c.policy.Match(c.userCred) if got != c.want { t.Errorf("%#v %#v got %v want %v", c.policy, c.userCred, got, c.want) } From 78faed91a4ee0ebda6608f55bc6ae924195df0e3 Mon Sep 17 00:00:00 2001 From: Qiu Jian Date: Thu, 21 Mar 2019 11:29:30 +0800 Subject: [PATCH 10/10] fix: SyncOnce should invalidate policy cache --- pkg/cloudcommon/policy/policy.go | 1 + pkg/util/hashcache/cache.go | 19 +++++++++++++++++++ pkg/util/hashcache/cache_test.go | 11 +++++++++++ 3 files changed, 31 insertions(+) diff --git a/pkg/cloudcommon/policy/policy.go b/pkg/cloudcommon/policy/policy.go index f9bed82332..bceebdb928 100644 --- a/pkg/cloudcommon/policy/policy.go +++ b/pkg/cloudcommon/policy/policy.go @@ -147,6 +147,7 @@ func (manager *SPolicyManager) SyncOnce() error { manager.adminPolicies = adminPolicies manager.lastSync = time.Now() + manager.cache.Invalidate() return nil } diff --git a/pkg/util/hashcache/cache.go b/pkg/util/hashcache/cache.go index fe053bddaf..03698f0380 100644 --- a/pkg/util/hashcache/cache.go +++ b/pkg/util/hashcache/cache.go @@ -8,12 +8,22 @@ import ( "time" ) +var initTime = time.Time{} + type cacheNode struct { key string expire time.Time value interface{} } +func (node *cacheNode) reset() { + if !node.expire.IsZero() { + node.key = "" + node.expire = initTime + node.value = nil + } +} + type Cache struct { table []cacheNode lock *sync.Mutex @@ -101,3 +111,12 @@ func (c *Cache) AtomicSet(key string, val interface{}) { defer c.lock.Unlock() c.Set(key, val) } + +func (c *Cache) Invalidate() { + c.lock.Lock() + defer c.lock.Unlock() + + for i := range c.table { + c.table[i].reset() + } +} diff --git a/pkg/util/hashcache/cache_test.go b/pkg/util/hashcache/cache_test.go index 3394e9ed14..44d57b849c 100644 --- a/pkg/util/hashcache/cache_test.go +++ b/pkg/util/hashcache/cache_test.go @@ -46,4 +46,15 @@ func TestCache(t *testing.T) { if v != nil { t.Errorf("key 123 shoud expire") } + + c.Set("123", 1234) + v = c.Get("123") + if v == nil || v.(int) != 1234 { + t.Error("Key 123 not found") + } + c.Invalidate() + v = c.Get("123") + if v != nil { + t.Error("Key 123 should not found") + } }