Merge pull request #10948 from rainzm/agent/fix

Makes the process of installing the agent more rigorous
This commit is contained in:
Zexi Li
2021-04-27 20:10:49 +08:00
committed by GitHub
12 changed files with 573 additions and 277 deletions
+124 -54
View File
@@ -28,9 +28,11 @@ import (
ansible_api "yunion.io/x/onecloud/pkg/apis/ansible"
cloudproxy_api "yunion.io/x/onecloud/pkg/apis/cloudproxy"
comapi "yunion.io/x/onecloud/pkg/apis/compute"
devtool_api "yunion.io/x/onecloud/pkg/apis/devtool"
"yunion.io/x/onecloud/pkg/cloudcommon/db"
"yunion.io/x/onecloud/pkg/cloudcommon/db/taskman"
"yunion.io/x/onecloud/pkg/devtool/models"
"yunion.io/x/onecloud/pkg/devtool/utils"
"yunion.io/x/onecloud/pkg/mcclient"
"yunion.io/x/onecloud/pkg/mcclient/auth"
"yunion.io/x/onecloud/pkg/mcclient/modules"
@@ -45,23 +47,36 @@ func init() {
taskman.RegisterTask(ApplyScriptTask{})
}
var ErrServerNotSshable = errors.Error("server is not sshable")
func (self *ApplyScriptTask) taskFailed(ctx context.Context, sa *models.SScriptApply, sar *models.SScriptApplyRecord, err error) {
err = sa.StopApply(self.UserCred, sar, false, err.Error())
var failCode string
switch errors.Cause(err) {
case ErrServerNotSshable:
failCode = devtool_api.SCRIPT_APPLY_RECORD_FAILCODE_SSHABLE
case utils.ErrCannotReachInfluxbd:
failCode = devtool_api.SCRIPT_APPLY_RECORD_FAILCODE_INFLUXDB
default:
failCode = devtool_api.SCRIPT_APPLY_RECORD_FAILCODE_OTHERS
}
err = sa.StopApply(self.UserCred, sar, false, failCode, err.Error())
if err != nil {
log.Errorf("unable to StopApply script %s to server %s", sa.ScriptId, sa.GuestId)
self.SetStageFailed(ctx, jsonutils.NewString(err.Error()))
return
}
// restart
err = sa.StartApply(ctx, self.UserCred)
if err != nil {
log.Errorf("unable to StartApply script %s to server %s", sa.ScriptId, sa.GuestId)
if failCode == devtool_api.SCRIPT_APPLY_RECORD_FAILCODE_OTHERS {
// restart
err = sa.StartApply(ctx, self.UserCred)
if err != nil {
log.Errorf("unable to StartApply script %s to server %s", sa.ScriptId, sa.GuestId)
}
}
self.SetStageFailed(ctx, jsonutils.NewString(err.Error()))
}
func (self *ApplyScriptTask) taskSuccess(ctx context.Context, sa *models.SScriptApply, sar *models.SScriptApplyRecord) {
err := sa.StopApply(self.UserCred, sar, true, "")
err := sa.StopApply(self.UserCred, sar, true, "", "")
if err != nil {
log.Errorf("unable to StopApply script %s to server %s", sa.ScriptId, sa.GuestId)
self.SetStageComplete(ctx, nil)
@@ -103,7 +118,11 @@ func (self *ApplyScriptTask) OnInit(ctx context.Context, obj db.IStandaloneModel
return
}
if !sshable.ok {
self.taskFailed(ctx, sa, sar, fmt.Errorf("server %s is not sshable: %s", serverDetail.Id, sshable.reason))
var err error = ErrServerNotSshable
if len(sshable.reason) > 0 {
err = errors.Wrap(err, sshable.reason)
}
self.taskFailed(ctx, sa, sar, err)
return
}
// make sure user
@@ -116,49 +135,79 @@ func (self *ApplyScriptTask) OnInit(ctx context.Context, obj db.IStandaloneModel
default:
user = "cloudroot"
}
// create local forward
createP := jsonutils.NewDict()
createP.Set("type", jsonutils.NewString(cloudproxy_api.FORWARD_TYPE_LOCAL))
createP.Set("remote_port", jsonutils.NewInt(22))
createP.Set("server_id", jsonutils.NewString(serverDetail.Id))
forward, err := cloudproxy.Forwards.PerformClassAction(session, "create-from-server", createP)
if err != nil {
self.taskFailed(ctx, sa, sar, errors.Wrapf(err, "fail to create local forward from server %q", serverDetail.Id))
return
var host ansible_api.AnsibleHost
var forwardId string
if len(sshable.proxyEndpointId) == 0 {
host = ansible_api.AnsibleHost{
User: user,
IP: sshable.host,
Port: sshable.port,
Name: serverDetail.Name,
}
} else {
// create local forward
createP := jsonutils.NewDict()
createP.Set("type", jsonutils.NewString(cloudproxy_api.FORWARD_TYPE_LOCAL))
createP.Set("remote_port", jsonutils.NewInt(22))
createP.Set("server_id", jsonutils.NewString(serverDetail.Id))
forward, err := cloudproxy.Forwards.PerformClassAction(session, "create-from-server", createP)
if err != nil {
self.taskFailed(ctx, sa, sar, errors.Wrapf(err, "fail to create local forward from server %q", serverDetail.Id))
return
}
port, _ := forward.Int("bind_port")
forwardId, _ = forward.GetString("id")
agentId, _ := forward.GetString("proxy_agent_id")
agent, err := cloudproxy.ProxyAgents.Get(session, agentId, nil)
if err != nil {
self.clearLocalForward(session, forwardId)
self.taskFailed(ctx, sa, sar, errors.Wrapf(err, "fail to get proxy agent %q", agentId))
return
}
address, _ := agent.GetString("advertise_addr")
// check proxy forward
if ok := self.ensureLocalForwardWork(address, int(port)); !ok {
self.clearLocalForward(session, forwardId)
self.taskFailed(ctx, sa, sar, errors.Error("The created local forward is actually not usable"))
return
}
host = ansible_api.AnsibleHost{
User: user,
IP: address,
Port: int(port),
Name: serverDetail.Name,
}
}
port, _ := forward.Int("bind_port")
forwardId, _ := forward.GetString("id")
agentId, _ := forward.GetString("proxy_agent_id")
agent, err := cloudproxy.ProxyAgents.Get(session, agentId, nil)
if err != nil {
self.clearLocalForward(session, forwardId)
self.taskFailed(ctx, sa, sar, errors.Wrapf(err, "fail to get proxy agent %q", agentId))
return
}
address, _ := agent.GetString("advertise_addr")
host := ansible_api.AnsibleHost{
User: user,
IP: address,
Port: int(port),
Name: serverDetail.Name,
}
// genrate args
params = jsonutils.NewDict()
params.Set("args", sa.Args)
if len(sa.ArgsGenerator) == 0 {
params.Set("args", sa.Args)
} else {
generator, ok := utils.GetArgGenerator(sa.ArgsGenerator)
if !ok {
params.Set("args", sa.Args)
}
arg, err := generator(ctx, sa.GuestId, sshable.proxyEndpointId, &host)
if err != nil {
self.clearLocalForward(session, forwardId)
self.taskFailed(ctx, sa, sar, err)
return
}
params.Set("args", jsonutils.Marshal(arg))
}
params.Set("host", jsonutils.Marshal(host))
// fetch ansible playbook reference id
updateData := jsonutils.NewDict()
updateData.Set("script_apply_record_id", jsonutils.NewString(sar.GetId()))
updateData.Set("proxy_forward_id", jsonutils.NewString(forwardId))
// check proxy forward
if ok := self.ensureLocalForwardWork(address, int(port)); !ok {
self.clearLocalForward(session, forwardId)
self.taskFailed(ctx, sa, sar, errors.Error("The created local forward is actually not usable"))
return
}
self.SetStage("OnAnsiblePlaybookComplete", updateData)
// Inject Task Header
session.Header = self.GetTaskRequestHeader()
_, err = modules.AnsiblePlaybookReference.PerformAction(session, s.PlaybookReferenceId, "run", params)
@@ -170,38 +219,59 @@ func (self *ApplyScriptTask) OnInit(ctx context.Context, obj db.IStandaloneModel
}
type sSSHable struct {
user string
ok bool
reason string
user string
proxyEndpointId string
proxyAgentId string
host string
port int
}
// func (self *ApplyScriptTask) ansibleHost(session modules.SS)
func (self *ApplyScriptTask) checkSshable(session *mcclient.ClientSession, serverId string) (sSSHable, error) {
data, err := modules.Servers.GetSpecific(session, serverId, "sshable", nil)
if err != nil {
return sSSHable{}, errors.Wrapf(err, "unable to get sshable info of server %s", serverId)
}
log.Debugf("data to chech sshable:\n %s", data)
methodTrieds, _ := data.GetArray("method_tried")
sshable := sSSHable{}
reasons := make([]string, 0, len(methodTrieds))
for _, methodTried := range methodTrieds {
ok, _ := methodTried.Bool("sshable")
if ok {
sshable.ok = true
break
var sshableOutput comapi.GuestSshableOutput
err = data.Unmarshal(&sshableOutput)
if err != nil {
return sSSHable{}, errors.Wrapf(err, "unable to marshal output of server sshable: %s", data)
}
sshable := sSSHable{
user: sshableOutput.User,
}
reasons := make([]string, 0, len(sshableOutput.MethodTried))
for _, methodTried := range sshableOutput.MethodTried {
if !methodTried.Sshable {
reasons = append(reasons, methodTried.Reason)
continue
}
sshable.ok = true
switch methodTried.Method {
case comapi.MethodDirect, comapi.MethodEIP, comapi.MethodDNAT:
sshable.host = methodTried.Host
sshable.port = methodTried.Port
case comapi.MethodProxyForward:
sshable.proxyAgentId = methodTried.ForwardDetails.ProxyAgentId
sshable.proxyEndpointId = methodTried.ForwardDetails.ProxyEndpointId
}
reason, _ := methodTried.GetString("reason")
reasons = append(reasons, reason)
}
if !sshable.ok {
sshable.reason = strings.Join(reasons, "; ")
} else {
sshable.user, _ = data.GetString("user")
}
return sshable, nil
}
func (self *ApplyScriptTask) clearLocalForward(s *mcclient.ClientSession, forwardId string) {
if len(forwardId) == 0 {
return
}
_, err := cloudproxy.Forwards.Delete(s, forwardId, nil)
if err != nil {
log.Errorf("unable to delete proxy forward %s", forwardId)