diff --git a/cmd/climc/shell/compute/servers.go b/cmd/climc/shell/compute/servers.go index 0cdf639ac6..ce40cb22a7 100644 --- a/cmd/climc/shell/compute/servers.go +++ b/cmd/climc/shell/compute/servers.go @@ -88,6 +88,7 @@ func init() { cmd.Perform("create-eip", &options.ServerCreateEipOptions{}) cmd.Perform("make-sshable", &options.ServerMakeSshableOptions{}) cmd.Perform("migrate-network", &options.ServerMigrateNetworkOptions{}) + cmd.Perform("set-sshport", &options.ServerSetSshportOptions{}) cmd.Get("vnc", new(options.ServerIdOptions)) cmd.Get("desc", new(options.ServerIdOptions)) diff --git a/pkg/apis/compute/guest_metadata.go b/pkg/apis/compute/guest_metadata.go index 04dc154342..1b6f4c00c2 100644 --- a/pkg/apis/compute/guest_metadata.go +++ b/pkg/apis/compute/guest_metadata.go @@ -18,6 +18,8 @@ const ( MIRROR_JOB = "__mirror_job_status" MIRROR_JOB_READY = "ready" MIRROR_JOB_FAILED = "failed" + + SSH_PORT = "__ssh_port" ) const BASE_INSTANCE_SNAPSHOT_ID = "__base_instance_snapshot_id" diff --git a/pkg/apis/compute/guest_sshable.go b/pkg/apis/compute/guest_sshable.go index d54d21fbe2..b7a99a2561 100644 --- a/pkg/apis/compute/guest_sshable.go +++ b/pkg/apis/compute/guest_sshable.go @@ -48,6 +48,7 @@ type GuestMakeSshableInput struct { User string PrivateKey string Password string + Port int } type GuestMakeSshableOutput struct { @@ -57,3 +58,7 @@ type GuestMakeSshableOutput struct { type GuestMakeSshableCmdOutput struct { ShellCmd string } + +type GuestSetSshPortInput struct { + Port int +} diff --git a/pkg/apis/compute/guests.go b/pkg/apis/compute/guests.go index 22cced81ae..275188e3cc 100644 --- a/pkg/apis/compute/guests.go +++ b/pkg/apis/compute/guests.go @@ -617,4 +617,6 @@ type ServerUpdateInput struct { SrcIpCheck *bool `json:"src_ip_check"` SrcMacCheck *bool `json:"src_mac_check"` + + SshPort int `json:"ssh_port"` } diff --git a/pkg/cloudproxy/models/proxy_endpoints.go b/pkg/cloudproxy/models/proxy_endpoints.go index ecb7433fb5..8b890f5673 100644 --- a/pkg/cloudproxy/models/proxy_endpoints.go +++ b/pkg/cloudproxy/models/proxy_endpoints.go @@ -16,6 +16,7 @@ package models import ( "context" + "strconv" "yunion.io/x/jsonutils" "yunion.io/x/log" @@ -101,10 +102,14 @@ func (man *SProxyEndpointManager) PerformCreateFromServer(ctx context.Context, u return nil, httperrors.NewGeneralError(err) } + port := 22 + if portStr, ok := serverInfo.Server.Metadata[compute_apis.SSH_PORT]; ok { + port, _ = strconv.Atoi(portStr) + } proxyendpoint := &SProxyEndpoint{ User: "cloudroot", Host: host, - Port: 22, + Port: port, PrivateKey: serverInfo.PrivateKey, IntranetIpAddr: nic.IpAddr, diff --git a/pkg/compute/models/guest_sshable.go b/pkg/compute/models/guest_sshable.go index 80a687c840..55efc176a6 100644 --- a/pkg/compute/models/guest_sshable.go +++ b/pkg/compute/models/guest_sshable.go @@ -17,11 +17,13 @@ package models import ( "context" "fmt" + "strconv" "strings" "time" "yunion.io/x/jsonutils" "yunion.io/x/log" + "yunion.io/x/pkg/errors" "yunion.io/x/pkg/tristate" "yunion.io/x/sqlchemy" @@ -132,6 +134,14 @@ func (guest *SGuest) sshableTryEach( network *SNetwork vpc *SVpc } + // make sure the ssh port + var sshPort int + if tryData.Port != 0 { + sshPort = tryData.Port + } else { + sshPort = guest.GetSshPort(userCred) + } + tryData.Port = sshPort var gnInfos []gnInfo for i := range gns { gn := &gns[i] @@ -169,7 +179,7 @@ func (guest *SGuest) sshableTryEach( for i := range gnInfos { gnInfo := &gnInfos[i] gn := gnInfo.guestNetwork - port := 22 + port := sshPort input := &cloudproxy_api.ForwardListInput{ Type: cloudproxy_api.FORWARD_TYPE_LOCAL, RemoteAddr: gn.IpAddr, @@ -200,7 +210,7 @@ func (guest *SGuest) sshableTryEach( fwdCreateInput := cloudproxy_api.ForwardCreateFromServerInput{ ServerId: guest.Id, Type: cloudproxy_api.FORWARD_TYPE_LOCAL, - RemotePort: 22, + RemotePort: sshPort, } fwdCreateParams := jsonutils.Marshal(fwdCreateInput) res, err := cloudproxy_module.Forwards.PerformClassAction(sess, "create-from-server", fwdCreateParams) @@ -248,7 +258,7 @@ func (guest *SGuest) sshableTryEach( natgwq := NatGatewayManager.Query().SubQuery() q := NatDEntryManager.Query(). Equals("internal_ip", gn.IpAddr). - Equals("internal_port", 22). + Equals("internal_port", sshPort). Equals("ip_protocol", "tcp") q = q.Join(natgwq, sqlchemy.AND( sqlchemy.In(natgwq.Field("vpc_id"), vpc.Id), @@ -316,7 +326,7 @@ func (guest *SGuest) sshableTryEip( methodData := compute_api.GuestSshableMethodData{ Method: compute_api.MethodEIP, Host: eip.IpAddr, - Port: 22, + Port: tryData.Port, } return guest.sshableTry( ctx, tryData, methodData, @@ -331,7 +341,7 @@ func (guest *SGuest) sshableTryDefaultVPC( methodData := compute_api.GuestSshableMethodData{ Method: compute_api.MethodDirect, Host: gn.IpAddr, - Port: 22, + Port: tryData.Port, } return guest.sshableTry( ctx, tryData, methodData, @@ -403,6 +413,7 @@ func (guest *SGuest) PerformMakeSshable( tryData := &GuestSshableTryData{ DryRun: true, + Port: input.Port, } if err := guest.sshableTryEach(ctx, userCred, tryData); err != nil { return output, httperrors.NewNotAcceptableError("searching for usable ssh address: %v", err) @@ -410,6 +421,13 @@ func (guest *SGuest) PerformMakeSshable( return output, httperrors.NewNotAcceptableError("no usable ssh address") } + // storage sshport + if input.Port != 0 { + err := guest.SetSshPort(ctx, userCred, input.Port) + if err != nil { + return output, errors.Wrap(err, "unable to set sshport for guest") + } + } host := ansible.Host{ Name: guest.Name, } @@ -544,3 +562,27 @@ fi } return output, nil } + +func (guest *SGuest) GetSshPort(userCred mcclient.TokenCredential) int { + portStr := guest.GetMetadata(compute_api.SSH_PORT, userCred) + if portStr == "" { + return 22 + } + port, _ := strconv.Atoi(portStr) + return port +} + +func (guest *SGuest) SetSshPort(ctx context.Context, userCred mcclient.TokenCredential, port int) error { + return guest.SetMetadata(ctx, compute_api.SSH_PORT, port, userCred) +} + +func (guest *SGuest) AllowPerformSetSshport(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject) bool { + return guest.IsOwner(userCred) || db.IsAdminAllowPerform(userCred, guest, "set-sshport") +} + +func (guest *SGuest) PerformSetSshPort(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, input compute_api.GuestSetSshPortInput) (jsonutils.JSONObject, error) { + if input.Port < 0 { + return nil, httperrors.NewInputParameterError("invalid port") + } + return nil, guest.SetSshPort(ctx, userCred, input.Port) +} diff --git a/pkg/compute/models/guests.go b/pkg/compute/models/guests.go index f911e1fee2..9dbe91365c 100644 --- a/pkg/compute/models/guests.go +++ b/pkg/compute/models/guests.go @@ -1614,6 +1614,12 @@ func (self *SGuest) PostUpdate(ctx context.Context, userCred mcclient.TokenCrede log.Errorf("StartRemoteUpdateTask fail: %s", err) } } + if port, err := data.Int("ssh_port"); err != nil { + err := self.SetSshPort(ctx, userCred, int(port)) + if err != nil { + log.Errorf("unable to set sshport for guest %s", self.GetId()) + } + } } func (manager *SGuestManager) checkCreateQuota( diff --git a/pkg/mcclient/options/servers.go b/pkg/mcclient/options/servers.go index 9b75a6512d..d3c4b6a19f 100644 --- a/pkg/mcclient/options/servers.go +++ b/pkg/mcclient/options/servers.go @@ -1123,6 +1123,16 @@ func (opts *ServerMakeSshableOptions) Params() (jsonutils.JSONObject, error) { return jsonutils.Marshal(opts), nil } +type ServerSetSshportOptions struct { + BaseIdOptions + + Port int `help:"ssh port" default:"22"` +} + +func (opts *ServerSetSshportOptions) Params() (jsonutils.JSONObject, error) { + return jsonutils.Marshal(opts), nil +} + type ServerMigrateNetworkOptions struct { BaseIdOptions