Files
cloudpods/pkg/keystone/models/endpoints.go
2019-10-12 12:46:48 +08:00

365 lines
12 KiB
Go

// 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 models
import (
"context"
"database/sql"
"fmt"
"yunion.io/x/jsonutils"
"yunion.io/x/pkg/tristate"
"yunion.io/x/sqlchemy"
api "yunion.io/x/onecloud/pkg/apis/identity"
"yunion.io/x/onecloud/pkg/cloudcommon/db"
"yunion.io/x/onecloud/pkg/httperrors"
"yunion.io/x/onecloud/pkg/mcclient"
"yunion.io/x/onecloud/pkg/util/logclient"
"yunion.io/x/onecloud/pkg/util/stringutils2"
)
type SEndpointManager struct {
db.SStandaloneResourceBaseManager
}
var EndpointManager *SEndpointManager
func init() {
EndpointManager = &SEndpointManager{
SStandaloneResourceBaseManager: db.NewStandaloneResourceBaseManager(
SEndpoint{},
"endpoint",
"endpoint",
"endpoints",
),
}
EndpointManager.SetVirtualObject(EndpointManager)
}
/*
+--------------------+--------------+------+-----+---------+-------+
| Field | Type | Null | Key | Default | Extra |
+--------------------+--------------+------+-----+---------+-------+
| id | varchar(64) | NO | PRI | NULL | |
| legacy_endpoint_id | varchar(64) | YES | | NULL | |
| interface | varchar(8) | NO | | NULL | |
| service_id | varchar(64) | NO | MUL | NULL | |
| url | text | NO | | NULL | |
| extra | text | YES | | NULL | |
| enabled | tinyint(1) | NO | | 1 | |
| region_id | varchar(255) | YES | MUL | NULL | |
| created_at | datetime | YES | | NULL | |
+--------------------+--------------+------+-----+---------+-------+
*/
type SEndpoint struct {
db.SStandaloneResourceBase
LegacyEndpointId string `width:"64" charset:"ascii" nullable:"true"`
Interface string `width:"8" charset:"ascii" nullable:"false" list:"admin" create:"admin_required"`
ServiceId string `width:"64" charset:"ascii" nullable:"false" list:"admin" create:"admin_required"`
Url string `charset:"utf8" nullable:"false" list:"admin" update:"admin" create:"admin_required"`
Extra *jsonutils.JSONDict `nullable:"true"`
Enabled tristate.TriState `nullable:"false" default:"true" list:"admin" update:"admin" create:"admin_optional"`
RegionId string `width:"255" charset:"utf8" nullable:"true" list:"admin" create:"admin_required"`
}
func (manager *SEndpointManager) InitializeData() error {
q := manager.Query()
q = q.IsNullOrEmpty("name")
eps := make([]SEndpoint, 0)
err := db.FetchModelObjects(manager, q, &eps)
if err != nil {
return err
}
for i := range eps {
name, _ := eps[i].Extra.GetString("name")
desc, _ := eps[i].Extra.GetString("description")
if len(name) == 0 {
serv := eps[i].getService()
if serv != nil {
name = fmt.Sprintf("%s-%s", serv.Type, eps[i].Interface)
}
}
if len(name) > 0 {
db.Update(&eps[i], func() error {
eps[i].Name = name
eps[i].Description = desc
return nil
})
}
}
return nil
}
func (ep *SEndpoint) getService() *SService {
srvObj, err := ServiceManager.FetchById(ep.ServiceId)
if err != nil {
return nil
}
return srvObj.(*SService)
}
type SEndpointExtended struct {
Id string
Name string
Interface string
Url string
Region string
RegionId string
ServiceId string
ServiceType string
ServiceName string
}
type SServiceCatalog []SEndpointExtended
func (manager *SEndpointManager) FetchAll() (SServiceCatalog, error) {
endpoints := manager.Query().SubQuery()
services := ServiceManager.Query().SubQuery()
regions := RegionManager.Query().SubQuery()
q := endpoints.Query(
endpoints.Field("id"),
endpoints.Field("name"),
endpoints.Field("interface"),
endpoints.Field("url"),
endpoints.Field("region_id"),
regions.Field("name", "region"),
endpoints.Field("service_id"),
services.Field("type", "service_type"),
services.Field("name", "service_name"),
)
q = q.Join(regions, sqlchemy.Equals(endpoints.Field("region_id"), regions.Field("id")))
q = q.Join(services, sqlchemy.Equals(endpoints.Field("service_id"), services.Field("id")))
q = q.Filter(sqlchemy.IsTrue(endpoints.Field("enabled")))
q = q.Filter(sqlchemy.IsTrue(services.Field("enabled")))
eps := make([]SEndpointExtended, 0)
err := q.All(&eps)
if err != nil && err != sql.ErrNoRows {
return nil, err
}
return eps, nil
}
func (cata SServiceCatalog) GetKeystoneCatalogV3() mcclient.KeystoneServiceCatalogV3 {
ksCata := make(map[string]mcclient.KeystoneServiceV3)
for i := range cata {
srvId := cata[i].ServiceId
srv, ok := ksCata[srvId]
if !ok {
srv = mcclient.KeystoneServiceV3{
Id: srvId,
Name: cata[i].ServiceName,
Type: cata[i].ServiceType,
Endpoints: make([]mcclient.KeystoneEndpointV3, 0),
}
}
ep := mcclient.KeystoneEndpointV3{
Id: cata[i].Id,
Name: cata[i].Name,
Interface: cata[i].Interface,
Region: cata[i].Region,
RegionId: cata[i].RegionId,
Url: cata[i].Url,
}
srv.Endpoints = append(srv.Endpoints, ep)
ksCata[cata[i].ServiceId] = srv
}
results := make([]mcclient.KeystoneServiceV3, 0)
for k := range ksCata {
results = append(results, ksCata[k])
}
return results
}
func (cata SServiceCatalog) GetKeystoneCatalogV2() mcclient.KeystoneServiceCatalogV2 {
ksCata := make(map[string]mcclient.KeystoneServiceV2)
for i := range cata {
srvId := cata[i].ServiceId
srv, ok := ksCata[srvId]
if !ok {
srv = mcclient.KeystoneServiceV2{
Name: cata[i].ServiceName,
Type: cata[i].ServiceType,
Endpoints: make([]mcclient.KeystoneEndpointV2, 0),
}
}
findIdx := -1
for j := range srv.Endpoints {
if srv.Endpoints[j].Region == cata[i].RegionId {
findIdx = j
}
}
if findIdx < 0 {
ep := mcclient.KeystoneEndpointV2{
Id: cata[i].Id,
Region: cata[i].RegionId,
}
srv.Endpoints = append(srv.Endpoints, ep)
findIdx = len(srv.Endpoints) - 1
}
switch cata[i].Interface {
case api.EndpointInterfacePublic:
srv.Endpoints[findIdx].PublicURL = cata[i].Url
case api.EndpointInterfaceInternal:
srv.Endpoints[findIdx].InternalURL = cata[i].Url
case api.EndpointInterfaceAdmin:
srv.Endpoints[findIdx].AdminURL = cata[i].Url
}
ksCata[cata[i].ServiceId] = srv
}
results := make([]mcclient.KeystoneServiceV2, 0)
for k := range ksCata {
results = append(results, ksCata[k])
}
return results
}
func (manager *SEndpointManager) FetchCustomizeColumns(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, objs []db.IModel, fields stringutils2.SSortedStrings) []*jsonutils.JSONDict {
rows := manager.SStandaloneResourceBaseManager.FetchCustomizeColumns(ctx, userCred, query, objs, fields)
serviceIds := stringutils2.SSortedStrings{}
for i := range objs {
ep := objs[i].(*SEndpoint)
serviceIds = stringutils2.Append(serviceIds, ep.ServiceId)
}
if len(fields) == 0 || fields.Contains("service_name") || fields.Contains("service_type") {
svs := fetchServices(serviceIds)
if svs != nil {
for i := range rows {
ep := objs[i].(*SEndpoint)
if srv, ok := svs[ep.ServiceId]; ok {
if len(fields) == 0 || fields.Contains("service_name") {
rows[i].Add(jsonutils.NewString(srv.Name), "service_name")
}
if len(fields) == 0 || fields.Contains("service_type") {
rows[i].Add(jsonutils.NewString(srv.Type), "service_type")
}
}
}
}
}
return rows
}
func fetchServices(srvIds []string) map[string]SService {
q := ServiceManager.Query().In("id", srvIds)
srvs := make([]SService, 0)
err := db.FetchModelObjects(ServiceManager, q, &srvs)
if err != nil {
return nil
}
ret := make(map[string]SService)
for i := range srvs {
ret[srvs[i].Id] = srvs[i]
}
return ret
}
func (endpoint *SEndpoint) ValidateDeleteCondition(ctx context.Context) error {
if endpoint.Enabled.IsTrue() {
return httperrors.NewInvalidStatusError("endpoint is enabled")
}
return endpoint.SStandaloneResourceBase.ValidateDeleteCondition(ctx)
}
func (endpoint *SEndpoint) GetCustomizeColumns(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject) *jsonutils.JSONDict {
extra := endpoint.SStandaloneResourceBase.GetCustomizeColumns(ctx, userCred, query)
return endpointExtra(endpoint, extra)
}
func (endpoint *SEndpoint) GetExtraDetails(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject) (*jsonutils.JSONDict, error) {
extra, err := endpoint.SStandaloneResourceBase.GetExtraDetails(ctx, userCred, query)
if err != nil {
return nil, err
}
return endpointExtra(endpoint, extra), nil
}
func endpointExtra(endpoint *SEndpoint, extra *jsonutils.JSONDict) *jsonutils.JSONDict {
return extra
}
func (manager *SEndpointManager) ValidateCreateData(ctx context.Context, userCred mcclient.TokenCredential, ownerId mcclient.IIdentityProvider, query jsonutils.JSONObject, data *jsonutils.JSONDict) (*jsonutils.JSONDict, error) {
infname, _ := data.GetString("interface")
if len(infname) == 0 {
return nil, httperrors.NewInputParameterError("missing input field interface")
}
serviceStr := jsonutils.GetAnyString(data, []string{"service_id", "service"})
if len(serviceStr) > 0 {
servObj, err := ServiceManager.FetchByIdOrName(userCred, serviceStr)
if err != nil {
if err == sql.ErrNoRows {
return nil, httperrors.NewResourceNotFoundError2(ServiceManager.Keyword(), serviceStr)
} else {
return nil, httperrors.NewGeneralError(err)
}
}
service := servObj.(*SService)
if !data.Contains("name") {
data.Set("name", jsonutils.NewString(fmt.Sprintf("%s-%s", service.Type, infname)))
}
data.Set("service_id", jsonutils.NewString(service.Id))
} else {
return nil, httperrors.NewInputParameterError("missing input field service/service_id")
}
return manager.SStandaloneResourceBaseManager.ValidateCreateData(ctx, userCred, ownerId, query, data)
}
func (manager *SEndpointManager) ListItemFilter(ctx context.Context, q *sqlchemy.SQuery, userCred mcclient.TokenCredential, query jsonutils.JSONObject) (*sqlchemy.SQuery, error) {
q, err := manager.SStandaloneResourceBaseManager.ListItemFilter(ctx, q, userCred, query)
if err != nil {
return nil, err
}
svcStr := jsonutils.GetAnyString(query, []string{"service", "service_id"})
if len(svcStr) > 0 {
svcObj, err := ServiceManager.FetchByIdOrName(userCred, svcStr)
if err != nil {
if err == sql.ErrNoRows {
return nil, httperrors.NewResourceNotFoundError2(ServiceManager.Keyword(), svcStr)
} else {
return nil, httperrors.NewGeneralError(err)
}
}
subq := ServiceManager.Query("id").Equals("id", svcObj.GetId())
q = q.Equals("service_id", subq.SubQuery())
}
return q, nil
}
func (endpoint *SEndpoint) PostCreate(ctx context.Context, userCred mcclient.TokenCredential, ownerId mcclient.IIdentityProvider, query jsonutils.JSONObject, data jsonutils.JSONObject) {
endpoint.SStandaloneResourceBase.PostCreate(ctx, userCred, ownerId, query, data)
logclient.AddActionLogWithContext(ctx, endpoint, logclient.ACT_CREATE, data, userCred, true)
refreshDefaultClientServiceCatalog()
}
func (endpoint *SEndpoint) PostUpdate(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, data jsonutils.JSONObject) {
endpoint.SStandaloneResourceBase.PostUpdate(ctx, userCred, query, data)
logclient.AddActionLogWithContext(ctx, endpoint, logclient.ACT_UPDATE, data, userCred, true)
refreshDefaultClientServiceCatalog()
}
func (endpoint *SEndpoint) PostDelete(ctx context.Context, userCred mcclient.TokenCredential) {
endpoint.SStandaloneResourceBase.PostDelete(ctx, userCred)
logclient.AddActionLogWithContext(ctx, endpoint, logclient.ACT_DELETE, nil, userCred, true)
refreshDefaultClientServiceCatalog()
}