// Copyright 2019 Yunion // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package modulebase import ( "fmt" "sort" "strings" "sync" "yunion.io/x/jsonutils" "yunion.io/x/log" "yunion.io/x/pkg/errors" "yunion.io/x/pkg/util/httputils" "yunion.io/x/pkg/util/printutils" "yunion.io/x/pkg/util/sets" "yunion.io/x/onecloud/pkg/mcclient" ) type IBaseManager interface { Version() string // GetApiVersion() string GetKeyword() string KeyString() string ServiceType() string EndpointType() string GetColumns(session *mcclient.ClientSession) []string List(session *mcclient.ClientSession, params jsonutils.JSONObject) (*printutils.ListResult, error) } type ManagerContext struct { InstanceManager Manager InstanceId string } type Manager interface { IBaseManager /* resource list GET / e.g GET /alarms querystring stores in params e.g. GET /alarms?limit=20&offset=20&search=test return list of resources in json format { "": [ {object details}, {object details}, ...] }, limit: 20, offset: 20, total: 2000} */ // List(session *mcclient.ClientSession, params jsonutils.JSONObject) (*ListResult, error) /* resource list in a context GET ///?querystring e.g. GET /nodes/1/labels?name=xxx ListInContext(s, params, &modules.Labels, label_id) return: { "": [ {object details}, {object details}, ...] }, limit: 20, offset: 20, total: 2000} */ ListInContext(session *mcclient.ClientSession, params jsonutils.JSONObject, ctx Manager, ctxid string) (*printutils.ListResult, error) ListInContexts(session *mcclient.ClientSession, params jsonutils.JSONObject, ctxs []ManagerContext) (*printutils.ListResult, error) /* GET // e.g GET /alarams/1 */ Get(session *mcclient.ClientSession, id string, params jsonutils.JSONObject) (jsonutils.JSONObject, error) GetInContext(session *mcclient.ClientSession, id string, params jsonutils.JSONObject, ctx Manager, ctxid string) (jsonutils.JSONObject, error) GetInContexts(session *mcclient.ClientSession, id string, params jsonutils.JSONObject, ctxs []ManagerContext) (jsonutils.JSONObject, error) GetId(session *mcclient.ClientSession, id string, params jsonutils.JSONObject) (string, error) GetIdInContext(session *mcclient.ClientSession, id string, params jsonutils.JSONObject, ctx Manager, ctxid string) (string, error) GetIdInContexts(session *mcclient.ClientSession, id string, params jsonutils.JSONObject, ctxs []ManagerContext) (string, error) GetById(session *mcclient.ClientSession, id string, params jsonutils.JSONObject) (jsonutils.JSONObject, error) GetByIdInContext(session *mcclient.ClientSession, id string, params jsonutils.JSONObject, ctx Manager, ctxid string) (jsonutils.JSONObject, error) GetByIdInContexts(session *mcclient.ClientSession, id string, params jsonutils.JSONObject, ctxs []ManagerContext) (jsonutils.JSONObject, error) GetByName(session *mcclient.ClientSession, id string, params jsonutils.JSONObject) (jsonutils.JSONObject, error) GetByNameInContext(session *mcclient.ClientSession, id string, params jsonutils.JSONObject, ctx Manager, ctxid string) (jsonutils.JSONObject, error) GetByNameInContexts(session *mcclient.ClientSession, id string, params jsonutils.JSONObject, ctxs []ManagerContext) (jsonutils.JSONObject, error) /* HEAD // e.g HEAD /alarams/1 */ Head(session *mcclient.ClientSession, id string, params jsonutils.JSONObject) (jsonutils.JSONObject, error) HeadInContext(session *mcclient.ClientSession, id string, params jsonutils.JSONObject, ctx Manager, ctxid string) (jsonutils.JSONObject, error) HeadInContexts(session *mcclient.ClientSession, id string, params jsonutils.JSONObject, ctxs []ManagerContext) (jsonutils.JSONObject, error) BatchGet(session *mcclient.ClientSession, idlist []string, params jsonutils.JSONObject) []printutils.SubmitResult BatchGetInContext(session *mcclient.ClientSession, idlist []string, params jsonutils.JSONObject, ctx Manager, ctxid string) []printutils.SubmitResult BatchGetInContexts(session *mcclient.ClientSession, idlist []string, params jsonutils.JSONObject, ctxs []ManagerContext) []printutils.SubmitResult GetSpecific(session *mcclient.ClientSession, id string, spec string, params jsonutils.JSONObject) (jsonutils.JSONObject, error) GetSpecificInContext(session *mcclient.ClientSession, id string, spec string, params jsonutils.JSONObject, ctx Manager, ctxid string) (jsonutils.JSONObject, error) GetSpecificInContexts(session *mcclient.ClientSession, id string, spec string, params jsonutils.JSONObject, ctxs []ManagerContext) (jsonutils.JSONObject, error) /* POST // e.g POST /alarams/1 */ Create(session *mcclient.ClientSession, params jsonutils.JSONObject) (jsonutils.JSONObject, error) CreateInContext(session *mcclient.ClientSession, params jsonutils.JSONObject, ctx Manager, ctxid string) (jsonutils.JSONObject, error) CreateInContexts(session *mcclient.ClientSession, params jsonutils.JSONObject, ctxs []ManagerContext) (jsonutils.JSONObject, error) BatchCreate(session *mcclient.ClientSession, params jsonutils.JSONObject, count int) []printutils.SubmitResult BatchCreateInContext(session *mcclient.ClientSession, params jsonutils.JSONObject, count int, ctx Manager, ctxid string) []printutils.SubmitResult BatchCreateInContexts(session *mcclient.ClientSession, params jsonutils.JSONObject, count int, ctxs []ManagerContext) []printutils.SubmitResult Update(session *mcclient.ClientSession, id string, params jsonutils.JSONObject) (jsonutils.JSONObject, error) Put(session *mcclient.ClientSession, id string, params jsonutils.JSONObject) (jsonutils.JSONObject, error) PutSpecific(session *mcclient.ClientSession, id string, spec string, query jsonutils.JSONObject, params jsonutils.JSONObject) (jsonutils.JSONObject, error) PutInContext(session *mcclient.ClientSession, id string, params jsonutils.JSONObject, ctx Manager, ctxid string) (jsonutils.JSONObject, error) PutInContexts(session *mcclient.ClientSession, id string, params jsonutils.JSONObject, ctxs []ManagerContext) (jsonutils.JSONObject, error) BatchUpdate(session *mcclient.ClientSession, idlist []string, params jsonutils.JSONObject) []printutils.SubmitResult BatchParamsUpdate(session *mcclient.ClientSession, idlist []string, params []jsonutils.JSONObject) []printutils.SubmitResult BatchPut(session *mcclient.ClientSession, idlist []string, params jsonutils.JSONObject) []printutils.SubmitResult BatchPutInContext(session *mcclient.ClientSession, idlist []string, params jsonutils.JSONObject, ctx Manager, ctxid string) []printutils.SubmitResult BatchPutInContexts(session *mcclient.ClientSession, idlist []string, params jsonutils.JSONObject, ctxs []ManagerContext) []printutils.SubmitResult Patch(session *mcclient.ClientSession, id string, params jsonutils.JSONObject) (jsonutils.JSONObject, error) PatchInContext(session *mcclient.ClientSession, id string, params jsonutils.JSONObject, ctx Manager, ctxid string) (jsonutils.JSONObject, error) PatchInContexts(session *mcclient.ClientSession, id string, params jsonutils.JSONObject, ctxs []ManagerContext) (jsonutils.JSONObject, error) BatchPatch(session *mcclient.ClientSession, idlist []string, params jsonutils.JSONObject) []printutils.SubmitResult BatchPatchInContext(session *mcclient.ClientSession, idlist []string, params jsonutils.JSONObject, ctx Manager, ctxid string) []printutils.SubmitResult BatchPatchInContexts(session *mcclient.ClientSession, idlist []string, params jsonutils.JSONObject, ctxs []ManagerContext) []printutils.SubmitResult PerformAction(session *mcclient.ClientSession, id string, action string, params jsonutils.JSONObject) (jsonutils.JSONObject, error) PerformClassAction(session *mcclient.ClientSession, action string, params jsonutils.JSONObject) (jsonutils.JSONObject, error) PerformActionInContext(session *mcclient.ClientSession, id string, action string, params jsonutils.JSONObject, ctx Manager, ctxid string) (jsonutils.JSONObject, error) PerformActionInContexts(session *mcclient.ClientSession, id string, action string, params jsonutils.JSONObject, ctxs []ManagerContext) (jsonutils.JSONObject, error) BatchPerformAction(session *mcclient.ClientSession, idlist []string, action string, params jsonutils.JSONObject) []printutils.SubmitResult BatchPerformActionInContext(session *mcclient.ClientSession, idlist []string, action string, params jsonutils.JSONObject, ctx Manager, ctxid string) []printutils.SubmitResult BatchPerformActionInContexts(session *mcclient.ClientSession, idlist []string, action string, params jsonutils.JSONObject, ctxs []ManagerContext) []printutils.SubmitResult Delete(session *mcclient.ClientSession, id string, body jsonutils.JSONObject) (jsonutils.JSONObject, error) DeleteWithParam(session *mcclient.ClientSession, id string, query jsonutils.JSONObject, body jsonutils.JSONObject) (jsonutils.JSONObject, error) DeleteInContext(session *mcclient.ClientSession, id string, body jsonutils.JSONObject, ctx Manager, ctxid string) (jsonutils.JSONObject, error) DeleteInContextWithParam(session *mcclient.ClientSession, id string, query jsonutils.JSONObject, body jsonutils.JSONObject, ctx Manager, ctxid string) (jsonutils.JSONObject, error) DeleteInContexts(session *mcclient.ClientSession, id string, body jsonutils.JSONObject, ctxs []ManagerContext) (jsonutils.JSONObject, error) DeleteInContextsWithParam(session *mcclient.ClientSession, id string, query jsonutils.JSONObject, body jsonutils.JSONObject, ctxs []ManagerContext) (jsonutils.JSONObject, error) BatchDelete(session *mcclient.ClientSession, idlist []string, body jsonutils.JSONObject) []printutils.SubmitResult BatchDeleteWithParam(session *mcclient.ClientSession, idlist []string, query jsonutils.JSONObject, body jsonutils.JSONObject) []printutils.SubmitResult BatchDeleteInContext(session *mcclient.ClientSession, idlist []string, body jsonutils.JSONObject, ctx Manager, ctxid string) []printutils.SubmitResult BatchDeleteInContextWithParam(session *mcclient.ClientSession, idlist []string, query jsonutils.JSONObject, body jsonutils.JSONObject, ctx Manager, ctxid string) []printutils.SubmitResult BatchDeleteInContexts(session *mcclient.ClientSession, idlist []string, body jsonutils.JSONObject, ctxs []ManagerContext) []printutils.SubmitResult BatchDeleteInContextsWithParam(session *mcclient.ClientSession, idlist []string, query jsonutils.JSONObject, body jsonutils.JSONObject, ctxs []ManagerContext) []printutils.SubmitResult GetSpecificMethods() sets.String } type IResourceManager interface { Manager GetMetadata(session *mcclient.ClientSession, id string, params jsonutils.JSONObject) (jsonutils.JSONObject, error) SetMetadata(session *mcclient.ClientSession, id string, params jsonutils.JSONObject) (jsonutils.JSONObject, error) } type JointManager interface { IBaseManager MasterManager() Manager SlaveManager() Manager Get(s *mcclient.ClientSession, mid, sid string, params jsonutils.JSONObject) (jsonutils.JSONObject, error) ListDescendent(s *mcclient.ClientSession, mid string, params jsonutils.JSONObject) (*printutils.ListResult, error) ListDescendent2(s *mcclient.ClientSession, sid string, params jsonutils.JSONObject) (*printutils.ListResult, error) ListAscendent(s *mcclient.ClientSession, mid string, params jsonutils.JSONObject) (*printutils.ListResult, error) Attach(s *mcclient.ClientSession, mid, sid string, params jsonutils.JSONObject) (jsonutils.JSONObject, error) BatchAttach(s *mcclient.ClientSession, mid string, sids []string, params jsonutils.JSONObject) []printutils.SubmitResult BatchAttach2(s *mcclient.ClientSession, mid string, sids []string, params jsonutils.JSONObject) []printutils.SubmitResult Detach(s *mcclient.ClientSession, mid, sid string, query jsonutils.JSONObject) (jsonutils.JSONObject, error) BatchDetach(s *mcclient.ClientSession, mid string, sids []string) []printutils.SubmitResult BatchDetach2(s *mcclient.ClientSession, mid string, sids []string) []printutils.SubmitResult Update(s *mcclient.ClientSession, mid, sid string, query jsonutils.JSONObject, params jsonutils.JSONObject) (jsonutils.JSONObject, error) BatchUpdate(s *mcclient.ClientSession, mid string, sids []string, query jsonutils.JSONObject, params jsonutils.JSONObject) []printutils.SubmitResult Patch(s *mcclient.ClientSession, mid, sid string, query jsonutils.JSONObject, params jsonutils.JSONObject) (jsonutils.JSONObject, error) BatchPatch(s *mcclient.ClientSession, mid string, sids []string, query jsonutils.JSONObject, params jsonutils.JSONObject) []printutils.SubmitResult } var ( modules map[string][]IBaseManager jointModules map[string][]JointManager ) func resourceKey(mod IBaseManager) string { return fmt.Sprintf("%s-%s", mod.ServiceType(), mod.KeyString()) } func resourceKey2(mod IBaseManager) string { return mod.KeyString() } func jointResourceKey(mod1, mod2 IBaseManager) string { return fmt.Sprintf("%s-%s-%s", mod1.ServiceType(), mod1.KeyString(), mod2.KeyString()) } func jointResourceKey2(mod1, mod2 IBaseManager) string { return fmt.Sprintf("%s-%s", mod1.KeyString(), mod2.KeyString()) } func ensureModuleNotRegistered(mod, newMod IBaseManager, isFatal bool) bool { modSvcType := mod.ServiceType() newModSvcType := newMod.ServiceType() if mod == newMod { if isFatal { log.Fatalf("Module %#v duplicate registered, service type: %q", mod, modSvcType) } else { return false } } if modSvcType != newModSvcType { if isFatal { log.Fatalf("Module %#v already registered, service type is %q.\nSo new module %#v can't be registered, service type is %q", mod, modSvcType, newMod, newModSvcType) } else { return false } } return true } func Register(m IBaseManager) { if modules == nil { modules = make(map[string][]IBaseManager) } for i, key := range []string{ resourceKey(m), resourceKey2(m), } { fatal := false if i == 0 { fatal = true } mods, ok := modules[key] if !ok { mods = make([]IBaseManager, 0) } skip := false for i := range mods { if !ensureModuleNotRegistered(mods[i], m, fatal) { skip = true break } } if !skip { mods = append(mods, m) modules[key] = mods } } } func RegisterJointModule(mod IBaseManager) { jointMod, ok := mod.(JointManager) if ok { // also a joint manager for _, jointKey := range []string{ jointResourceKey(jointMod.MasterManager(), jointMod.SlaveManager()), jointResourceKey2(jointMod.MasterManager(), jointMod.SlaveManager()), } { jointMods, ok := jointModules[jointKey] if !ok { jointMods = make([]JointManager, 0) } skip := false for i := range jointMods { if !ensureModuleNotRegistered(jointMods[i], jointMod, false) { skip = true break } } if !skip { jointModules[jointKey] = append(jointMods, jointMod) } } } } var jointModulesLock *sync.Mutex func init() { jointModulesLock = &sync.Mutex{} } func registerAllJointModules() { jointModulesLock.Lock() defer jointModulesLock.Unlock() if jointModules == nil { jointModules = make(map[string][]JointManager) for modname := range modules { for i := range modules[modname] { RegisterJointModule(modules[modname][i]) } } } } func _getModule(session *mcclient.ClientSession, name string) (IBaseManager, error) { mods, ok := modules[name] if !ok { return nil, fmt.Errorf("No such module %s", name) } if len(mods) == 1 { return mods[0], nil } for _, mod := range mods { url, e := session.GetServiceURL(mod.ServiceType(), mod.EndpointType(), httputils.POST) if e != nil { return nil, errors.Wrap(e, "session.GetServiceURL") } _, ver := mcclient.SplitVersionedURL(url) log.Debugf("url: %s ver: %s mod.Version: %s", url, ver, mod.Version()) if strings.EqualFold(ver, mod.Version()) { return mod, nil } } return nil, fmt.Errorf("Version mismatch") } func GetModule(session *mcclient.ClientSession, name string) (Manager, error) { bm, e := _getModule(session, name) if e != nil { return nil, e } m, ok := bm.(Manager) if ok { return m, nil } else { return nil, fmt.Errorf("Module %s not a Manager", name) } } func GetJointModule(session *mcclient.ClientSession, name string) (JointManager, error) { bm, e := _getModule(session, name) if e != nil { return nil, e } m, ok := bm.(JointManager) if ok { return m, nil } else { return nil, fmt.Errorf("Module %s not a Manager", name) } } func GetJointModule2(session *mcclient.ClientSession, mod1 Manager, mod2 Manager) (JointManager, error) { registerAllJointModules() for _, key := range []string{ jointResourceKey(mod1, mod2), jointResourceKey2(mod1, mod2), } { mods, ok := jointModules[key] if !ok { continue } for _, mod := range mods { url, e := session.GetServiceVersionURL(mod.ServiceType(), mod.EndpointType(), httputils.POST) if e != nil { return nil, e } _, ver := mcclient.SplitVersionedURL(url) if strings.EqualFold(ver, mod.Version()) { return mod, nil } } return nil, fmt.Errorf("Version mismatch") } return nil, fmt.Errorf("No such joint module: %s", jointResourceKey(mod1, mod2)) } func GetRegisterdModules() ([]string, []string) { registerAllJointModules() ret := make([]string, 0) for k := range modules { ret = append(ret, k) } sort.Strings(ret) ret2 := make([]string, 0) for k := range jointModules { ret2 = append(ret2, k) } sort.Strings(ret2) return ret, ret2 }