package models import ( "context" "encoding/base64" "fmt" "io" "net/http" "strings" "yunion.io/x/cloudmux/pkg/multicloud/objectstore" "yunion.io/x/jsonutils" "yunion.io/x/pkg/errors" "yunion.io/x/sqlchemy" api "yunion.io/x/onecloud/pkg/apis/compute" "yunion.io/x/onecloud/pkg/cloudcommon/db" "yunion.io/x/onecloud/pkg/cloudcommon/db/lockman" "yunion.io/x/onecloud/pkg/httperrors" "yunion.io/x/onecloud/pkg/mcclient" "yunion.io/x/onecloud/pkg/util/logclient" ) type SGuestScreenDumpManager struct { db.SResourceBaseManager } var GuestScreenDumpManager *SGuestScreenDumpManager func init() { db.InitManager(func() { GuestScreenDumpManager = &SGuestScreenDumpManager{ SResourceBaseManager: db.NewResourceBaseManager( SGuestScreenDump{}, "guest_screen_dumps_tbl", "guest_screen_dump", "guest_screen_dumps", ), } GuestScreenDumpManager.SetVirtualObject(GuestScreenDumpManager) GuestScreenDumpManager.TableSpec().AddIndex(true, "guest_id") }) } type SGuestScreenDump struct { db.SResourceBase RowId int64 `primary:"true" auto_increment:"true" list:"user"` GuestId string `width:"36" charset:"ascii" nullable:"false" list:"user" create:"required"` Name string `width:"64" charset:"ascii" nullable:"true" list:"user"` // s3 config S3AccessKey string `width:"64" charset:"ascii" nullable:"true"` S3SecretKey string `width:"64" charset:"ascii" nullable:"true"` S3Endpoint string `width:"64" charset:"ascii" nullable:"true" list:"user"` S3BucketName string `width:"64" charset:"ascii" nullable:"true" list:"user"` S3UseSsl bool `default:"false" list:"user" create:"optional"` } func (manager *SGuestScreenDumpManager) ListItemFilter( ctx context.Context, q *sqlchemy.SQuery, userCred mcclient.TokenCredential, query api.GuestScreenDumpListInput, ) (*sqlchemy.SQuery, error) { if query.Server != "" { iGuest, err := GuestManager.FetchByIdOrName(ctx, userCred, query.Server) if err != nil { return q, errors.Wrap(err, "fetch guest") } q = q.Equals("guest_id", iGuest.GetId()) } return q, nil } func (self *SGuestScreenDump) Delete(ctx context.Context, userCred mcclient.TokenCredential) error { return db.DeleteModel(ctx, userCred, self) } func (self *SGuest) SaveGuestScreenDump(ctx context.Context, userCred mcclient.TokenCredential, screenDumpInfo *api.SGuestScreenDumpInfo) (*SGuestScreenDump, error) { sd := new(SGuestScreenDump) sd.SetModelManager(GuestScreenDumpManager, sd) sd.GuestId = self.GetId() sd.S3SecretKey = base64.StdEncoding.EncodeToString([]byte(screenDumpInfo.S3SecretKey)) sd.S3Endpoint = screenDumpInfo.S3Endpoint sd.S3BucketName = screenDumpInfo.S3BucketName sd.S3AccessKey = base64.StdEncoding.EncodeToString([]byte(screenDumpInfo.S3AccessKey)) sd.Name = screenDumpInfo.S3ObjectName lockman.LockClass(ctx, GuestScreenDumpManager, self.ProjectId) defer lockman.ReleaseClass(ctx, GuestScreenDumpManager, self.ProjectId) err := GuestScreenDumpManager.TableSpec().Insert(ctx, sd) if err != nil { return nil, errors.Wrap(err, "save guest screen_dump") } db.OpsLog.LogEvent(self, db.ACT_GUEST_SCREEN_DUMP, sd.Name, userCred) logclient.AddSimpleActionLog(self, logclient.ACT_GUEST_SCREEN_DUMP, sd.Name, userCred, true) return sd, nil } func (self *SGuest) GetDetailsScreenDumpShow(ctx context.Context, userCred mcclient.TokenCredential, input *api.GetDetailsGuestScreenDumpInput) (*api.GetDetailsGuestScreenDumpOutput, error) { if input.ObjectName == "" { return nil, httperrors.NewMissingParameterError("object_name") } q := GuestScreenDumpManager.Query() q = q.Equals("guest_id", self.Id) q = q.Equals("name", input.ObjectName) screenDump := new(SGuestScreenDump) err := q.First(screenDump) if err != nil { return nil, errors.Wrap(err, "query screenDump") } ak, _ := base64.StdEncoding.DecodeString(screenDump.S3AccessKey) sk, _ := base64.StdEncoding.DecodeString(screenDump.S3SecretKey) url := screenDump.S3Endpoint if !strings.HasPrefix(url, "http://") && !strings.HasPrefix(url, "https://") { prefix := "http://" if screenDump.S3UseSsl { prefix = "https://" } url = prefix + url } cfg := objectstore.NewObjectStoreClientConfig(url, string(ak), string(sk)) s3Client, err := objectstore.NewObjectStoreClient(cfg) if err != nil { return nil, errors.Wrap(err, "new minio client") } bucket, err := s3Client.GetIBucketByName(screenDump.S3BucketName) if err != nil { return nil, errors.Wrapf(err, "get bucket %s", screenDump.S3BucketName) } irc, err := bucket.GetObject(ctx, screenDump.Name, nil) if err != nil { return nil, errors.Wrapf(err, "get object %s", screenDump.Name) } defer irc.Close() obj, err := io.ReadAll(irc) if err != nil { return nil, errors.Wrapf(err, "read object %s", screenDump.Name) } ret := new(api.GetDetailsGuestScreenDumpOutput) contentType := http.DetectContentType(obj) base64Encoded := base64.StdEncoding.EncodeToString(obj) ret.ScreenDump = fmt.Sprintf("data:%s;base64,%s", contentType, base64Encoded) ret.ScreenDump = base64.StdEncoding.EncodeToString(obj) ret.GuestId = self.Id ret.Name = screenDump.Name return ret, nil } func (self *SGuest) PerformScreenDump(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, data jsonutils.JSONObject) (jsonutils.JSONObject, error) { if self.PowerStates != api.VM_POWER_STATES_ON { return nil, httperrors.NewBadRequestError("can't use qga in vm status: %s", self.Status) } host, _ := self.GetHost() driver, err := self.GetDriver() if err != nil { return nil, err } return driver.RequestGuestScreenDump(ctx, userCred, nil, host, self) }