fix: 1. abstract baseagent, 2. extend baseagent to baremetalagent

This commit is contained in:
Qiu Jian
2019-03-19 12:28:21 +08:00
parent f71e167128
commit 090984f9d2
4 changed files with 388 additions and 288 deletions
+52 -287
View File
@@ -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
}
+1 -1
View File
@@ -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 {
+1
View File
@@ -30,6 +30,7 @@ func (s *BaremetalService) StartService() {
cloudcommon.ServeForeverWithCleanup(app, &o.Options.CommonOptions, func() {
tasks.OnStop()
baremetal.Stop()
})
}
+334
View File
@@ -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()
}