diff --git a/Makefile b/Makefile index 89c1a4e839..f0c600b3ae 100644 --- a/Makefile +++ b/Makefile @@ -76,7 +76,7 @@ install: prepare_dir gencopyright: - @sh scripts/gencopyright.sh pkg cmd + @bash scripts/gencopyright.sh pkg cmd test: @go test $(GO_BUILD_FLAGS) $(shell go list ./... | egrep -v 'host-image|hostimage') diff --git a/cmd/climc/shell/cloudaccounts.go b/cmd/climc/shell/cloudaccounts.go index 49cdd60906..af31c7500e 100644 --- a/cmd/climc/shell/cloudaccounts.go +++ b/cmd/climc/shell/cloudaccounts.go @@ -988,4 +988,22 @@ func init() { printObject(result) return nil }) + + type ClouaccountChangeOwnerOptions struct { + ID string `help:"ID or name of cloudaccount" json:"-"` + ProjectDomain string `json:"project_domain" help:"target domain"` + } + R(&ClouaccountChangeOwnerOptions{}, "cloud-account-change-owner", "Change owner domain of cloudaccount", func(s *mcclient.ClientSession, args *ClouaccountChangeOwnerOptions) error { + if len(args.ProjectDomain) == 0 { + return fmt.Errorf("empty project_domain") + } + params := jsonutils.Marshal(args) + ret, err := modules.Cloudaccounts.PerformAction(s, args.ID, "change-owner", params) + if err != nil { + return err + } + printObject(ret) + return nil + }) + } diff --git a/cmd/climc/shell/hosts.go b/cmd/climc/shell/hosts.go index 470fe53022..476ce978b7 100644 --- a/cmd/climc/shell/hosts.go +++ b/cmd/climc/shell/hosts.go @@ -706,4 +706,21 @@ func init() { } return nil }) + + type HostChangeOwnerOptions struct { + ID string `help:"ID or name of host" json:"-"` + ProjectDomain string `json:"project_domain" help:"target domain"` + } + R(&HostChangeOwnerOptions{}, "host-change-owner", "Change owner domain of host", func(s *mcclient.ClientSession, args *HostChangeOwnerOptions) error { + if len(args.ProjectDomain) == 0 { + return fmt.Errorf("empty project_domain") + } + params := jsonutils.Marshal(args) + ret, err := modules.Hosts.PerformAction(s, args.ID, "change-owner", params) + if err != nil { + return err + } + printObject(ret) + return nil + }) } diff --git a/pkg/apis/billing/billing.go b/pkg/apis/billing/billing.go index a45b7bfd2c..d02dd61ae8 100644 --- a/pkg/apis/billing/billing.go +++ b/pkg/apis/billing/billing.go @@ -1,3 +1,17 @@ +// 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 billing import "time" diff --git a/pkg/apis/cloudnet/doc.go b/pkg/apis/cloudnet/doc.go new file mode 100644 index 0000000000..9951146f9a --- /dev/null +++ b/pkg/apis/cloudnet/doc.go @@ -0,0 +1,15 @@ +// 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 cloudnet // import "yunion.io/x/onecloud/pkg/apis/cloudnet" diff --git a/pkg/apis/cloudnet/update.go b/pkg/apis/cloudnet/update.go new file mode 100644 index 0000000000..0444d14840 --- /dev/null +++ b/pkg/apis/cloudnet/update.go @@ -0,0 +1,69 @@ +// 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 cloudnet + +import "yunion.io/x/onecloud/pkg/apis" + +type RouterUpdateInput struct { + apis.StandaloneResourceBaseUpdateInput + + User string `json:"user"` + + Host string `json:"host"` + + Port *int `json:"port"` + + PrivateKey string `json:"private_key"` + + RealizeWgIfaces *bool `json:"realize_wg_ifaces"` + + RealizeRoutes *bool `json:"realize_routes"` + + RealizeRules *bool `json:"realize_rules"` + + OldEndpoint string `json:"_old_endpoint"` +} + +type RouteUpdateInput struct { + apis.StandaloneResourceBaseUpdateInput + + Network string `json:"network"` + + Gateway string `json:"gateway"` +} + +type RuleUpdateInput struct { + apis.StandaloneResourceBaseUpdateInput + + Prio int `json:"prio"` + + MatchSrcNet string `json:"match_src_net"` + + MatchDestNet string `json:"match_dest_net"` + + MatchProto string `json:"match_proto"` + + MatchSrcPort int `json:"match_src_port"` + + MatchDestPort int `json:"match_dest_port"` + + MatchInIfname string `json:"match_in_ifname"` + + MatchOutIfname string `json:"match_out_ifname"` + + Action string `json:"action"` + + ActionOptions string `json:"action_options"` +} diff --git a/pkg/apis/compute/baremetalagent.go b/pkg/apis/compute/baremetalagent.go index 86ebc6f16d..ad03dc79de 100644 --- a/pkg/apis/compute/baremetalagent.go +++ b/pkg/apis/compute/baremetalagent.go @@ -22,3 +22,25 @@ type BaremetalagentDetails struct { SBaremetalagent } + +type BaremetalagentCreateInput struct { + apis.StandaloneResourceCreateInput + + ZoneResourceInput + + AccessIp string `json:"access_ip"` + ManagerUri string `json:"manager_uri"` + AgentType string `json:"agent_type"` + Version string `json:"version"` +} + +type BaremetalagentUpdateInput struct { + apis.StandaloneResourceBaseUpdateInput + + ZoneResourceInput + + AccessIp string `json:"access_ip"` + ManagerUri string `json:"manager_uri"` + AgentType string `json:"agent_type"` + Version string `json:"version"` +} diff --git a/pkg/apis/compute/bucket.go b/pkg/apis/compute/bucket.go index 6860b01963..4ff12957f0 100644 --- a/pkg/apis/compute/bucket.go +++ b/pkg/apis/compute/bucket.go @@ -116,3 +116,7 @@ type BucketListInput struct { type BucketSyncstatusInput struct { } + +type BucketUpdateInput struct { + apis.VirtualResourceBaseUpdateInput +} diff --git a/pkg/apis/compute/cloudaccount.go b/pkg/apis/compute/cloudaccount.go index fbab1c21ea..8414821fef 100644 --- a/pkg/apis/compute/cloudaccount.go +++ b/pkg/apis/compute/cloudaccount.go @@ -130,7 +130,7 @@ type CloudaccountResourceInfo struct { } type CloudaccountCreateInput struct { - apis.EnabledStatusDomainLevelResourceCreateInput + apis.EnabledStatusInfrasResourceBaseCreateInput // 指定云平台 // Qcloud: 腾讯云 @@ -183,7 +183,7 @@ type CloudaccountCreateInput struct { AutoCreateProject bool `json:"auto_create_project"` // 额外信息,例如账单的access key - Options *jsonutils.JSONObject `json:"options"` + Options *jsonutils.JSONDict `json:"options"` // 代理配置 ProxySettingId string `json:"proxy_setting_id"` @@ -209,7 +209,7 @@ func (i CloudaccountShareModeInput) Validate() error { } type CloudaccountListInput struct { - apis.EnabledStatusDomainLevelResourceListInput + apis.EnabledStatusInfrasResourceBaseListInput ManagedResourceListInput @@ -235,7 +235,7 @@ type ProviderProject struct { } type CloudaccountDetail struct { - apis.EnabledStatusDomainLevelResourceDetails + apis.EnabledStatusInfrasResourceBaseDetails SCloudaccount // 子订阅项目信息 @@ -295,3 +295,23 @@ type CloudaccountDetail struct { // example: 10 StoragecacheCount int `json:"storagecache_count,allowempty"` } + +type CloudaccountUpdateInput struct { + apis.EnabledStatusInfrasResourceBaseUpdateInput + + // 同步周期,单位为秒 + SyncIntervalSeconds *int64 `json:"sync_interval_seconds"` + + // 待更新的options key/value + Options *jsonutils.JSONDict `json:"options"` + // 带删除的options key + RemoveOptions []string `json:"remove_options"` +} + +type CloudaccountPerformPublicInput struct { + apis.PerformPublicInput + + // 共享模式,可能值为provider_domain, system + // example: provider_domain + ShareMode string `json:"share_mode"` +} diff --git a/pkg/apis/compute/cloudprovider.go b/pkg/apis/compute/cloudprovider.go index 61ebb6e3ea..2ad34576c3 100644 --- a/pkg/apis/compute/cloudprovider.go +++ b/pkg/apis/compute/cloudprovider.go @@ -139,10 +139,8 @@ type CloudproviderDetails struct { Brand string `json:"brand"` } -type ManagedResourceListInput struct { - apis.DomainizedResourceListInput - CloudenvResourceListInput - +// 云订阅输入参数 +type CloudproviderResourceInput struct { // 列出关联指定云订阅(ID或Name)的资源 Cloudprovider string `json:"cloudprovider"` // List objects belonging to the cloud provider @@ -158,6 +156,13 @@ type ManagedResourceListInput struct { // Deprecated // description: this param will be deprecate at 3.0 CloudproviderId string `json:"cloudprovider_id" deprecated-by:"cloudprovider"` +} + +type ManagedResourceListInput struct { + apis.DomainizedResourceListInput + CloudenvResourceListInput + + CloudproviderResourceInput // 列出关联指定云账号(ID或Name)的资源 Cloudaccount string `json:"cloudaccount"` @@ -235,3 +240,10 @@ type SyncableBaseResourceListInput struct { // 同步状态 SyncStatus []string `json:"sync_status"` } + +type CloudproviderUpdateInput struct { + apis.EnabledStatusStandaloneResourceBaseUpdateInput +} + +type CloudproviderCreateInput struct { +} diff --git a/pkg/apis/compute/disk.go b/pkg/apis/compute/disk.go index daeb905f2a..f75fe0b451 100644 --- a/pkg/apis/compute/disk.go +++ b/pkg/apis/compute/disk.go @@ -219,3 +219,10 @@ type DiskResourceInfo struct { type DiskSyncstatusInput struct { } + +type DiskUpdateInput struct { + apis.VirtualResourceBaseUpdateInput + + // 磁盘类型 + DiskType string `json:"disk_type"` +} diff --git a/pkg/apis/compute/dnsrecord.go b/pkg/apis/compute/dnsrecord.go new file mode 100644 index 0000000000..83abfc5cf3 --- /dev/null +++ b/pkg/apis/compute/dnsrecord.go @@ -0,0 +1,18 @@ +// 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 compute + +type DnsRecordUpdateInput struct { +} diff --git a/pkg/apis/compute/geo_input.go b/pkg/apis/compute/geo_input.go index 1668bd924b..f9c3fec9b4 100644 --- a/pkg/apis/compute/geo_input.go +++ b/pkg/apis/compute/geo_input.go @@ -18,11 +18,8 @@ import ( "yunion.io/x/onecloud/pkg/apis" ) -type RegionalFilterListInput struct { - // 过滤位于指定城市区域的资源 - City string `json:"city"` - - // 过滤处于指定区域内的资源 +type CloudregionResourceInput struct { + // 区域名称或ID Cloudregion string `json:"cloudregion"` // swagger:ignore // Deprecated @@ -36,6 +33,13 @@ type RegionalFilterListInput struct { // Deprecated // description: this param will be deprecate at 3.0 RegionId string `json:"region_id" deprecated-by:"cloudregion"` +} + +type RegionalFilterListInput struct { + // 过滤位于指定城市区域的资源 + City string `json:"city"` + + CloudregionResourceInput // 按区域名称过滤 OrderByRegion string `json:"order_by_region"` diff --git a/pkg/apis/compute/globalvpc.go b/pkg/apis/compute/globalvpc.go index 5227426848..dbdb38756a 100644 --- a/pkg/apis/compute/globalvpc.go +++ b/pkg/apis/compute/globalvpc.go @@ -19,11 +19,11 @@ import ( ) type GlobalVpcCreateInput struct { - apis.EnabledStatusStandaloneResourceCreateInput + apis.EnabledStatusInfrasResourceBaseCreateInput } type GlobalVpcDetails struct { - apis.EnabledStatusStandaloneResourceDetails + apis.EnabledStatusInfrasResourceBaseDetails SGlobalVpc } @@ -40,3 +40,11 @@ type GlobalVpcResourceListInput struct { // 以GlobalVpc的名称排序 OrderByGlobalvpc string `json:"order_by_globalvpc"` } + +type GlobalvpcUpdateInput struct { + apis.EnabledStatusInfrasResourceBaseUpdateInput +} + +type GlobalVpcListInput struct { + apis.EnabledStatusInfrasResourceBaseListInput +} diff --git a/pkg/apis/compute/guest_disk.go b/pkg/apis/compute/guest_disk.go index 86aa4d9bf7..27ac718aed 100644 --- a/pkg/apis/compute/guest_disk.go +++ b/pkg/apis/compute/guest_disk.go @@ -50,3 +50,19 @@ type GuestdiskListInput struct { AioMode []string `json:"aio_mode"` } + +type GuestdiskUpdateInput struct { + GuestJointBaseUpdateInput + + Driver string `json:"driver"` + + CacheMode string `json:"cache_mode"` + + AioMode string `json:"aio_mode"` + + Iops *int `json:"iops"` + + Bps *int `json:"bps"` + + Index *int8 `json:"index"` +} diff --git a/pkg/apis/compute/guestnetwork.go b/pkg/apis/compute/guestnetwork.go index 97af7072ba..fe37c0f25f 100644 --- a/pkg/apis/compute/guestnetwork.go +++ b/pkg/apis/compute/guestnetwork.go @@ -53,3 +53,13 @@ type GuestnetworkListInput struct { TeamWith []string `json:"team_with"` } + +type GuestnetworkUpdateInput struct { + GuestJointBaseUpdateInput + + Driver string `json:"driver"` + + BwLimit *int `json:"bw_limit"` + + Index *int8 `json:"index"` +} diff --git a/pkg/apis/compute/guests.go b/pkg/apis/compute/guests.go index 0d2522d8ac..87a4614b37 100644 --- a/pkg/apis/compute/guests.go +++ b/pkg/apis/compute/guests.go @@ -285,3 +285,7 @@ type GuestFilterListInput struct { // pattern:asc|desc OrderByServer string `json:"order_by_server"` } + +type GuestJointBaseUpdateInput struct { + apis.VirtualJointResourceBaseUpdateInput +} diff --git a/pkg/apis/compute/guesttemplate.go b/pkg/apis/compute/guesttemplate.go index c233b1faae..7629a00e55 100644 --- a/pkg/apis/compute/guesttemplate.go +++ b/pkg/apis/compute/guesttemplate.go @@ -20,9 +20,7 @@ import ( "yunion.io/x/onecloud/pkg/apis" ) -type GuesttemplateCreateInput struct { - apis.SharableVirtualResourceCreateInput - +type GuesttemplateInput struct { // description: the content of guest template // required: true Content jsonutils.JSONObject `json:"content"` @@ -35,6 +33,18 @@ type GuesttemplateCreateInput struct { ImageId string `json:"image_id"` } +type GuesttemplateCreateInput struct { + apis.SharableVirtualResourceCreateInput + + GuesttemplateInput +} + +type GuesttemplateUpdateInput struct { + apis.SharableVirtualResourceBaseUpdateInput + + GuesttemplateInput +} + type GuesttemplateDetails struct { apis.SharableVirtualResourceDetails SGuestTemplate diff --git a/pkg/apis/compute/host.go b/pkg/apis/compute/host.go index 657bc34a78..d01415bbe3 100644 --- a/pkg/apis/compute/host.go +++ b/pkg/apis/compute/host.go @@ -47,7 +47,7 @@ type DiskSpec struct { } type HostListInput struct { - apis.EnabledStatusStandaloneResourceListInput + apis.EnabledStatusInfrasResourceBaseListInput apis.ExternalizedResourceBaseListInput ManagedResourceListInput @@ -111,7 +111,7 @@ type HostListInput struct { } type HostDetails struct { - apis.EnabledStatusStandaloneResourceDetails + apis.EnabledStatusInfrasResourceBaseDetails ManagedResourceInfo ZoneResourceInfo @@ -226,3 +226,87 @@ type HostRegisterMetadata struct { OnKubernetes bool `json:"on_kubernetes"` Hostname string `json:"hostname"` } + +type HostAccessAttributes struct { + // 物理机管理URI + ManagerUri string `json:"manager_uri"` + + // 物理机管理口IP + AccessIp string `json:"access_ip"` + + // 物理机管理口MAC + AccessMac string `json:"access_mac"` + + // 物理机管理口IP子网 + AccessNet string `json:"access_net"` + // 物理机管理口二次网络 + AccessWire string `json:"access_wire"` +} + +type HostSizeAttributes struct { + // 内存大小(单位MB) + MemSize string `json:"mem_size"` + // 预留内存大小(单位MB) + MemReserved string `json:"mem_reserved"` + // CPU缓存大小(单位MB) + CpuCache string `json:"cpu_cache"` +} + +type HostIpmiAttributes struct { + // username + IpmiUsername string `json:"ipmi_username"` + // password + IpmiPassword string `json:"ipmi_password"` + // ip address + IpmiIpAddr string `json:"ipmi_ip_addr"` + // presence + IpmiPresent *bool `json:"ipmi_present"` + // lan channel + IpmiLanChannel *int `json:"ipmi_lan_channel"` + // verified + IpmiVerified *bool `json:"ipmi_verified"` + // Redfish API support + IpmiRedfishApi *bool `json:"ipmi_redfish_api"` + // Cdrom boot support + IpmiCdromBoot *bool `json:"ipmi_cdrom_boot"` + // ipmi_pxe_boot + IpmiPxeBoot *bool `json:"ipmi_pxe_boot"` +} + +type HostCreateInput struct { + apis.EnabledStatusInfrasResourceBaseCreateInput + + ZoneResourceInput + + HostAccessAttributes + HostSizeAttributes + HostIpmiAttributes + + // 新建带IPMI信息的物理机时不进行IPMI信息探测 + NoProbe *bool `json:"no_probe"` + + // host uuid + Uuid string `json:"uuid"` + + // Host类型 + HostType string `json:"host_type"` + + // 是否为裸金属 + IsBaremetal *bool `json:"is_baremetal"` +} + +type HostUpdateInput struct { + apis.EnabledStatusInfrasResourceBaseUpdateInput + + HostAccessAttributes + HostSizeAttributes + HostIpmiAttributes + + // IPMI info + IpmiInfo jsonutils.JSONObject `json:"ipmi_info"` + + // CPU超售比上限 + CpuCmtbound *float32 `json:"cpu_cmtbound"` + // 内存超售比上限 + MemCmtbound *float32 `json:"mem_cmtbound"` +} diff --git a/pkg/apis/compute/input.go b/pkg/apis/compute/input.go index 471ff282c4..004dbc65fb 100644 --- a/pkg/apis/compute/input.go +++ b/pkg/apis/compute/input.go @@ -72,7 +72,7 @@ type ExternalProjectListInput struct { } type RouteTableListInput struct { - apis.VirtualResourceListInput + apis.StatusInfrasResourceBaseListInput apis.ExternalizedResourceBaseListInput VpcFilterListInput @@ -94,7 +94,7 @@ type SnapshotPolicyCacheListInput struct { } type NetworkInterfaceListInput struct { - apis.StatusStandaloneResourceListInput + apis.StatusInfrasResourceBaseListInput apis.ExternalizedResourceBaseListInput ManagedResourceListInput diff --git a/pkg/apis/compute/nat.go b/pkg/apis/compute/nat.go index 69d18460ab..6e04916ecb 100644 --- a/pkg/apis/compute/nat.go +++ b/pkg/apis/compute/nat.go @@ -65,7 +65,7 @@ type SimpleNetwork struct { } type NatgatewayDetails struct { - apis.StatusStandaloneResourceDetails + apis.StatusInfrasResourceBaseDetails VpcResourceInfo diff --git a/pkg/apis/compute/natgateway.go b/pkg/apis/compute/natgateway.go index e7035842a8..82de740055 100644 --- a/pkg/apis/compute/natgateway.go +++ b/pkg/apis/compute/natgateway.go @@ -39,7 +39,7 @@ const ( ) type NatGetewayListInput struct { - apis.StatusStandaloneResourceListInput + apis.StatusInfrasResourceBaseListInput apis.ExternalizedResourceBaseListInput VpcFilterListInput @@ -48,7 +48,7 @@ type NatGetewayListInput struct { } type NatEntryListInput struct { - apis.StatusStandaloneResourceListInput + apis.StatusInfrasResourceBaseListInput apis.ExternalizedResourceBaseListInput NatGatewayFilterListInput ManagedResourceListInput @@ -94,7 +94,7 @@ type NatGatewayFilterListInput struct { } type NatEntryDetails struct { - apis.StatusStandaloneResourceDetails + apis.StatusInfrasResourceBaseDetails NatGatewayResourceInfo // NAT ENTRY的真实名称?? diff --git a/pkg/apis/compute/network.go b/pkg/apis/compute/network.go index 9120e207b4..22ff1b0445 100644 --- a/pkg/apis/compute/network.go +++ b/pkg/apis/compute/network.go @@ -18,25 +18,6 @@ import ( "yunion.io/x/onecloud/pkg/apis" ) -type VpcFilterListInputBase struct { - // 过滤关联此VPC(ID或Name)的资源 - Vpc string `json:"vpc"` - // swagger:ignore - // Deprecated - // filter by vpc Id - VpcId string `json:"vpc_id" deprecated-by:"vpc"` - - // 按VPC名称排序 - // pattern:asc|desc - OrderByVpc string `json:"order_by_vpc"` -} - -type VpcFilterListInput struct { - VpcFilterListInputBase - RegionalFilterListInput - ManagedResourceListInput -} - type WireFilterListBase struct { // 过滤连接此二层网络(ID或Name)的资源 Wire string `json:"wire"` @@ -278,3 +259,31 @@ type NetworkTryCreateNetworkInput struct { type NetworkSyncInput struct { apis.Meta } + +type NetworkUpdateInput struct { + apis.SharableVirtualResourceBaseUpdateInput + + // 起始IP地址 + GuestIpStart string `json:"guest_ip_start"` + // 接收IP地址 + GuestIpEnd string `json:"guest_ip_end"` + // 掩码 + GuestIpMask *int8 `json:"guest_ip_mask"` + // 网关地址 + GuestGateway string `json:"guest_gateway"` + // DNS + GuestDns string `json:"guest_dns"` + // allow multiple dhcp, seperated by "," + GuestDhcp string `json:"guest_dhcp"` + + GuestDomain string `json:"guest_domain"` + + VlanId *int `json:"vlan_id"` + + // 服务器类型 + // example: server + ServerType string `json:"server_type"` + + // 分配策略 + AllocPolicy string `json:"alloc_policy"` +} diff --git a/pkg/apis/compute/networkinterface.go b/pkg/apis/compute/networkinterface.go index 802bba1c6a..e51739fff9 100644 --- a/pkg/apis/compute/networkinterface.go +++ b/pkg/apis/compute/networkinterface.go @@ -30,7 +30,7 @@ type NetworkInterfaceNetworkInfo struct { } type NetworkInterfaceDetails struct { - apis.StatusStandaloneResourceDetails + apis.StatusInfrasResourceBaseDetails ManagedResourceInfo CloudregionResourceInfo diff --git a/pkg/apis/compute/routetable.go b/pkg/apis/compute/routetable.go index 7f78aa0b89..e0679088a5 100644 --- a/pkg/apis/compute/routetable.go +++ b/pkg/apis/compute/routetable.go @@ -14,11 +14,101 @@ package compute -import "yunion.io/x/onecloud/pkg/apis" +import ( + "net" + "reflect" + "strings" + + "yunion.io/x/pkg/errors" + "yunion.io/x/pkg/gotypes" + + "yunion.io/x/jsonutils" + "yunion.io/x/onecloud/pkg/apis" + "yunion.io/x/onecloud/pkg/httperrors" +) type RouteTableDetails struct { - apis.VirtualResourceDetails + apis.StatusInfrasResourceBaseDetails VpcResourceInfo SRouteTable } + +type SRoute struct { + Type string `json:"type"` + Cidr string `json:"cidr"` + NextHopType string `json:"next_hop_type"` + NextHopId string `json:"next_hop_id"` +} + +func (route *SRoute) Validate() error { + if strings.Index(route.Cidr, "/") > 0 { + _, ipNet, err := net.ParseCIDR(route.Cidr) + if err != nil { + return errors.Wrapf(httperrors.ErrInputParameter, "net.ParseCIDR %s", err) + } + // normalize from 192.168.1.3/24 to 192.168.1.0/24 + route.Cidr = ipNet.String() + } else { + ip := net.ParseIP(route.Cidr).To4() + if ip == nil { + return errors.Wrapf(httperrors.ErrInputParameter, "invalid addr %s", route.Cidr) + } + } + return nil +} + +type SRoutes []*SRoute + +func (routes SRoutes) String() string { + return jsonutils.Marshal(routes).String() +} + +func (routes SRoutes) IsZero() bool { + if len(routes) == 0 { + return true + } + return false +} + +func (routes *SRoutes) Validate() error { + if routes == nil { + *routes = SRoutes{} + return nil + } + + found := map[string]struct{}{} + for _, route := range *routes { + if err := route.Validate(); err != nil { + return err + } + if _, ok := found[route.Cidr]; ok { + // error so that the user has a chance to deal with comments + return httperrors.NewInputParameterError("duplicate route cidr %s", route.Cidr) + } + // TODO aliyun: check overlap with System type route + found[route.Cidr] = struct{}{} + } + return nil +} + +type RouteTableCreateInput struct { + apis.StatusInfrasResourceBaseCreateInput + + VpcResourceInput + + Type string `json:"type"` + Routes *SRoutes `json:"routes"` +} + +type RouteTableUpdateInput struct { + apis.StatusInfrasResourceBaseUpdateInput + + Routes *SRoutes `json:"routes"` +} + +func init() { + gotypes.RegisterSerializable(reflect.TypeOf(&SRoutes{}), func() gotypes.ISerializable { + return &SRoutes{} + }) +} diff --git a/pkg/apis/compute/serversku.go b/pkg/apis/compute/serversku.go index 5735414507..084d066e31 100644 --- a/pkg/apis/compute/serversku.go +++ b/pkg/apis/compute/serversku.go @@ -14,7 +14,9 @@ package compute -import "yunion.io/x/onecloud/pkg/apis" +import ( + "yunion.io/x/onecloud/pkg/apis" +) type ServerSkuCreateInput struct { apis.StatusStandaloneResourceCreateInput @@ -139,3 +141,45 @@ type ServerSkuDetails struct { // 绑定云主机数量 TotalGuestCount int `json:"total_guest_count"` } + +type ServerSkuUpdateInput struct { + apis.StatusStandaloneResourceBaseUpdateInput + + InstanceTypeFamily string `json:"instance_type_family"` + + InstanceTypeCategory string `json:"instance_type_category"` + + LocalCategory string `json:"local_category"` // 记录本地分类 + + OsName string `json:"os_name"` // Windows|Linux|Any + + SysDiskResizable *bool `json:"sys_disk_resizable"` + + SysDiskType string `json:"sys_disk_type"` + + SysDiskMinSizeGB *int `json:"sys_disk_min_size_gb"` // not required。 windows比较新的版本都是50G左右。 + + SysDiskMaxSizeGB *int `json:"sys_disk_max_size_gb"` // not required + + AttachedDiskType string `json:"attached_disk_type"` + + AttachedDiskSizeGB *int `json:"attached_disk_size_gb"` + + AttachedDiskCount *int `json:"attached_disk_count"` + + DataDiskTypes string `json:"data_disk_types"` + + DataDiskMaxCount *int `json:"data_disk_max_count"` + + NicType string `json:"nic_type"` + + NicMaxCount *int `json:"nic_max_count"` + + GpuAttachable *bool `json:"gpu_attachable"` + + GpuSpec string `json:"gpu_spec"` + + GpuCount *int `json:"gpu_count"` + + GpuMaxCount *int `json:"gpu_max_count"` +} diff --git a/pkg/apis/compute/storage.go b/pkg/apis/compute/storage.go index 8f0aa73e83..c1c62a41ca 100644 --- a/pkg/apis/compute/storage.go +++ b/pkg/apis/compute/storage.go @@ -21,7 +21,7 @@ import ( ) type StorageCreateInput struct { - apis.EnabledStatusStandaloneResourceCreateInput + apis.EnabledStatusInfrasResourceBaseCreateInput // 存储类型 // @@ -49,12 +49,7 @@ type StorageCreateInput struct { // required: true MediumType string `json:"medium_type"` - // 可用区名称或ID, 建议使用ID - // required: true - Zone string `json:"zone"` - - // swagger:ignore - ZoneId string + ZoneResourceInput // ceph认证主机, storage_type为 rbd 时,此参数为必传项 // 单个ip或以逗号分隔的多个ip具体可查询 /etc/ceph/ceph.conf 文件 @@ -128,7 +123,7 @@ type SStorageCapacityInfo struct { } type StorageDetails struct { - apis.EnabledStatusStandaloneResourceDetails + apis.EnabledStatusInfrasResourceBaseDetails ManagedResourceInfo ZoneResourceInfo diff --git a/pkg/apis/compute/storage_const.go b/pkg/apis/compute/storage_const.go index 7f9c01e2c5..aebc980e6f 100644 --- a/pkg/apis/compute/storage_const.go +++ b/pkg/apis/compute/storage_const.go @@ -168,7 +168,7 @@ type StorageShareFilterListInput struct { } type StorageListInput struct { - apis.EnabledStatusStandaloneResourceListInput + apis.EnabledStatusInfrasResourceBaseListInput apis.ExternalizedResourceBaseListInput ManagedResourceListInput diff --git a/pkg/apis/compute/vpc.go b/pkg/apis/compute/vpc.go index 040fca8e3d..d40f7a8b73 100644 --- a/pkg/apis/compute/vpc.go +++ b/pkg/apis/compute/vpc.go @@ -17,13 +17,13 @@ package compute import "yunion.io/x/onecloud/pkg/apis" type VpcDetails struct { - SVpc - - apis.EnabledStatusStandaloneResourceDetails + apis.EnabledStatusInfrasResourceBaseDetails ManagedResourceInfo CloudregionResourceInfo GlobalVpcResourceInfo + SVpc + // 二层网络数量 // example: 1 WireCount int `json:"wire_count"` @@ -62,3 +62,37 @@ type VpcResourceInfo struct { type VpcSyncstatusInput struct { } + +type VpcCreateInput struct { + apis.EnabledStatusInfrasResourceBaseCreateInput + + CloudregionResourceInput + + CloudproviderResourceInput + + // CIDR_BLOCK + CidrBlock string `json:"cidr_block"` +} + +type VpcResourceInput struct { + // 关联VPC(ID或Name) + Vpc string `json:"vpc"` + // swagger:ignore + // Deprecated + // filter by vpc Id + VpcId string `json:"vpc_id" deprecated-by:"vpc"` +} + +type VpcFilterListInputBase struct { + VpcResourceInput + + // 按VPC名称排序 + // pattern:asc|desc + OrderByVpc string `json:"order_by_vpc"` +} + +type VpcFilterListInput struct { + VpcFilterListInputBase + RegionalFilterListInput + ManagedResourceListInput +} diff --git a/pkg/apis/compute/vpcs_const.go b/pkg/apis/compute/vpcs_const.go index f4612254d7..3dbb8e3c97 100644 --- a/pkg/apis/compute/vpcs_const.go +++ b/pkg/apis/compute/vpcs_const.go @@ -46,7 +46,7 @@ type UsableVpcResourceListInput struct { } type VpcListInput struct { - apis.EnabledStatusStandaloneResourceListInput + apis.EnabledStatusInfrasResourceBaseListInput apis.ExternalizedResourceBaseListInput ManagedResourceListInput @@ -68,21 +68,6 @@ type VpcListInput struct { CidrBlock []string `json:"cidr_block"` } -type WireListInput struct { - apis.StandaloneResourceListInput - apis.ExternalizedResourceBaseListInput - - VpcFilterListInput - - ZonalFilterListBase - - HostResourceInput -} - -type GlobalVpcListInput struct { - apis.EnabledStatusStandaloneResourceListInput -} - const ( VPC_PROVIDER_OVN = "ovn" ) diff --git a/pkg/apis/compute/wire.go b/pkg/apis/compute/wire.go index 4d257f0bf2..d4f0724a9d 100644 --- a/pkg/apis/compute/wire.go +++ b/pkg/apis/compute/wire.go @@ -17,7 +17,7 @@ package compute import "yunion.io/x/onecloud/pkg/apis" type WireCreateInput struct { - apis.StandaloneResourceCreateInput + apis.InfrasResourceBaseCreateInput // 带宽大小,单位: Mbps // default: 0 @@ -29,22 +29,13 @@ type WireCreateInput struct { // default: 0 Mtu int `json:"mtu"` - // vpc名称或Id - // required: true - Vpc string `json:"vpc"` - // swagger:ignore - // Deprecated - VpcId string `json:"vpc_id" deprecated-by:"vpc"` + VpcResourceInput - // 可用区名称或Id - // required: true - Zone string `json:"zone"` - // swagger:ignore - ZoneId string + ZoneResourceInput } type WireDetails struct { - apis.StandaloneResourceDetails + apis.InfrasResourceBaseDetails VpcResourceInfo ZoneResourceInfoBase @@ -75,3 +66,25 @@ type WireResourceInfo struct { // 可用区 Zone string `json:"zone"` } + +type WireUpdateInput struct { + apis.InfrasResourceBaseUpdateInput + + // bandwidth in MB + Bandwidth int `json:"bandwidth"` + + // MTU + // example: 1500 + Mtu int `json:"mtu"` +} + +type WireListInput struct { + apis.InfrasResourceBaseListInput + apis.ExternalizedResourceBaseListInput + + VpcFilterListInput + + ZonalFilterListBase + + HostResourceInput +} diff --git a/pkg/apis/compute/zz_generated.model.go b/pkg/apis/compute/zz_generated.model.go index fe373a13f4..2acba72494 100644 --- a/pkg/apis/compute/zz_generated.model.go +++ b/pkg/apis/compute/zz_generated.model.go @@ -1585,14 +1585,6 @@ type SReservedip struct { Status string `json:"status"` } -// SRoute is an autogenerated struct via yunion.io/x/onecloud/pkg/compute/models.SRoute. -type SRoute struct { - Type string `json:"type"` - Cidr string `json:"cidr"` - NextHopType string `json:"next_hop_type"` - NextHopId string `json:"next_hop_id"` -} - // SRouteTable is an autogenerated struct via yunion.io/x/onecloud/pkg/compute/models.SRouteTable. type SRouteTable struct { apis.SVirtualResourceBase @@ -1602,9 +1594,6 @@ type SRouteTable struct { Routes *SRoutes `json:"routes"` } -// SRoutes is an autogenerated struct via yunion.io/x/onecloud/pkg/compute/models.SRoutes. -type SRoutes []*SRoute - // SSchedpolicy is an autogenerated struct via yunion.io/x/onecloud/pkg/compute/models.SSchedpolicy. type SSchedpolicy struct { apis.SStandaloneResourceBase diff --git a/pkg/apis/identity/consts.go b/pkg/apis/identity/consts.go index 3827240e53..04520281e4 100644 --- a/pkg/apis/identity/consts.go +++ b/pkg/apis/identity/consts.go @@ -95,6 +95,7 @@ var ( "enable_rbac", "non_default_domain_projects", "time_zone", + "domainized_namespace", }, } diff --git a/pkg/apis/identity/credential.go b/pkg/apis/identity/credential.go index 5279a0ed08..b43226dc34 100644 --- a/pkg/apis/identity/credential.go +++ b/pkg/apis/identity/credential.go @@ -25,3 +25,10 @@ type CredentialDetails struct { Domain string `json:"domain"` DomainId string `json:"domain_id"` } + +type CredentialUpdateInput struct { + apis.StandaloneResourceBaseUpdateInput + + // enabled + Enabled *bool `json:"enabled"` +} diff --git a/pkg/apis/identity/domain.go b/pkg/apis/identity/domain.go index 1d0e60d1cb..3f98daff29 100644 --- a/pkg/apis/identity/domain.go +++ b/pkg/apis/identity/domain.go @@ -29,3 +29,9 @@ type DomainDetails struct { PolicyCount int `json:"policy_count"` IdpCount int `json:"idp_count"` } + +type DomainUpdateInput struct { + apis.StandaloneResourceBaseUpdateInput + + Enabled *bool `json:"enabled"` +} diff --git a/pkg/apis/identity/input.go b/pkg/apis/identity/input.go index 914dbf35af..88c70cc4f7 100644 --- a/pkg/apis/identity/input.go +++ b/pkg/apis/identity/input.go @@ -15,6 +15,7 @@ package identity import ( + "yunion.io/x/jsonutils" "yunion.io/x/pkg/errors" "yunion.io/x/onecloud/pkg/apis" @@ -335,3 +336,65 @@ type ServiceListInput struct { // 是否启用/禁用 Enabled *bool `json:"enabled"` } + +type IdentityBaseUpdateInput struct { + apis.StandaloneResourceBaseUpdateInput +} + +type EnabledIdentityBaseUpdateInput struct { + IdentityBaseUpdateInput + + // 是否启用 + Enabled *bool `json:"enabled"` +} + +type GroupUpdateInput struct { + IdentityBaseUpdateInput + + // display name + Displayname string `json:"displayname"` +} + +type IdentityProviderUpdateInput struct { + apis.EnabledStatusStandaloneResourceBaseUpdateInput + + TargetDomainId string `json:"target_domain_id"` + + AutoCreateProject *bool `json:"auto_create_project"` + + SyncIntervalSeconds *int `json:"sync_interval_seconds"` +} + +type PolicyUpdateInput struct { + EnabledIdentityBaseUpdateInput + + Type string `json:"type"` + + Blob jsonutils.JSONObject `json:"blob"` +} + +type ProjectUpdateInput struct { + IdentityBaseUpdateInput +} + +type RoleUpdateInput struct { + IdentityBaseUpdateInput +} + +type UserUpdateInput struct { + EnabledIdentityBaseUpdateInput + + Email string `json:"email"` + + Mobile string `json:"mobile"` + + Displayname string `json:"displayname"` + + IsSystemAccount *bool `json:"is_system_account"` + + AllowWebConsole *bool `json:"allow_web_console"` + + EnableMfa *bool `json:"enable_mfa"` + + Password string `json:"password"` +} diff --git a/pkg/apis/identity/output.go b/pkg/apis/identity/output.go index 894351d824..bbfcd1c605 100644 --- a/pkg/apis/identity/output.go +++ b/pkg/apis/identity/output.go @@ -1,4 +1,5 @@ // Copyright 2019 Yunion +// 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. diff --git a/pkg/apis/input.go b/pkg/apis/input.go index 7a24809568..83700a76ba 100644 --- a/pkg/apis/input.go +++ b/pkg/apis/input.go @@ -120,23 +120,26 @@ type StatusStandaloneResourceCreateInput struct { type StandaloneResourceCreateInput struct { ResourceBaseCreateInput + // 资源名称,如果generate_name为空,则为必填项 // description: resource name, required if generated_name is not given // unique: true // required: true // example: test-network Name string `json:"name"` + // 生成资源名称的规则,如果name为空,则为必填项 // description: generated resource name, given a pattern to generate name, required if name is not given // unique: false // required: false // example: test### GenerateName string `json:"generate_name"` - // description: resource description + // 资源描述 // required: false // example: test create network Description string `json:"description"` + // 资源是否为模拟资源 // description: the resource is an emulated resource // required: false IsEmulated *bool `json:"is_emulated"` @@ -173,16 +176,19 @@ type GetDetailsStatusOutput struct { Status string `json:"status"` } -type PerformProjectPublicInput struct { +type PerformPublicInput struct { // 共享项目资源的共享范围,可能的值为:project, domain和system // pattern: project|domain|system Scope string `json:"scope"` // 如果共享范围为项目,则在此列表中指定共享的目标项目 SharedProjects []string `json:"shared_projects"` + + // 如果共享范围为域,则在此列表中指定共享的目标域 + SharedDomains []string `json:"shared_domains"` } -type PerformProjectPrivateInput struct { +type PerformPrivateInput struct { } type PerformChangeProjectOwnerInput struct { @@ -198,3 +204,17 @@ type PerformEnableInput struct { type PerformDisableInput struct { } + +type InfrasResourceBaseCreateInput struct { + DomainLevelResourceCreateInput +} + +type StatusInfrasResourceBaseCreateInput struct { + InfrasResourceBaseCreateInput + StatusBaseResourceCreateInput +} + +type EnabledStatusInfrasResourceBaseCreateInput struct { + StatusInfrasResourceBaseCreateInput + EnabledBaseResourceCreateInput +} diff --git a/pkg/apis/list.go b/pkg/apis/list.go index 3f17575f3f..4ea1945cdd 100644 --- a/pkg/apis/list.go +++ b/pkg/apis/list.go @@ -199,6 +199,8 @@ type EnabledResourceBaseListInput struct { type SharableResourceBaseListInput struct { // 以资源是否共享过滤列表 IsPublic *bool `json:"is_public"` + // 根据资源的共享范围过滤列表,可能值为:system, domain, project + PublicScope string `json:"public_scope"` } type DomainLevelResourceListInput struct { @@ -247,3 +249,18 @@ type DeletePreventableResourceBaseListInput struct { type ScopedResourceBaseListInput struct { ProjectizedResourceListInput } + +type InfrasResourceBaseListInput struct { + DomainLevelResourceListInput + SharableResourceBaseListInput +} + +type StatusInfrasResourceBaseListInput struct { + InfrasResourceBaseListInput + StatusResourceBaseListInput +} + +type EnabledStatusInfrasResourceBaseListInput struct { + StatusInfrasResourceBaseListInput + EnabledResourceBaseListInput +} diff --git a/pkg/apis/monitor/alert.go b/pkg/apis/monitor/alert.go index 45ad9c3e58..4303782a14 100644 --- a/pkg/apis/monitor/alert.go +++ b/pkg/apis/monitor/alert.go @@ -112,10 +112,10 @@ type AlertCreateInput struct { } type AlertUpdateInput struct { - apis.Meta + apis.VirtualResourceBaseUpdateInput + + Message *string `json:"message"` - // 报警名称 - Name *string `json:"name"` // 报警执行频率 Frequency *int64 `json:"frequency"` // 报警设置 diff --git a/pkg/apis/monitor/doc.go b/pkg/apis/monitor/doc.go index b7f781ca03..5694ea6439 100644 --- a/pkg/apis/monitor/doc.go +++ b/pkg/apis/monitor/doc.go @@ -1 +1,15 @@ +// 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 monitor // import "yunion.io/x/onecloud/pkg/apis/monitor" diff --git a/pkg/apis/monitor/meteralert.go b/pkg/apis/monitor/meteralert.go index f166656cbf..147e50299b 100644 --- a/pkg/apis/monitor/meteralert.go +++ b/pkg/apis/monitor/meteralert.go @@ -56,6 +56,8 @@ type MeterAlertDetails struct { } type MeterAlertUpdateInput struct { + V1AlertUpdateInput + // 比较运算符, 比如: >, <, >=, <= Comparator *string `json:"comparator"` // 报警阀值 diff --git a/pkg/apis/monitor/nodealert.go b/pkg/apis/monitor/nodealert.go index 6d5396edf1..a1ea1a93ba 100644 --- a/pkg/apis/monitor/nodealert.go +++ b/pkg/apis/monitor/nodealert.go @@ -180,6 +180,8 @@ type NodeAlertDetails struct { } type NodeAlertUpdateInput struct { + V1AlertUpdateInput + // 监控指标名称 Metric *string `json:"metric"` // 监控资源类型, 比如: guest, host @@ -203,3 +205,7 @@ type NodeAlertUpdateInput struct { // 通知接受者 Recipients *string `json:"recipients"` } + +type V1AlertUpdateInput struct { + AlertUpdateInput +} diff --git a/pkg/apis/output.go b/pkg/apis/output.go index cf6eedba2a..e89ae7c041 100644 --- a/pkg/apis/output.go +++ b/pkg/apis/output.go @@ -39,12 +39,26 @@ type ModelBaseShortDescDetail struct { type SharedProject struct { Id string `json:"id"` Name string `json:"name"` + + DomainId string `json:"domain_id"` + Domain string `json:"domain"` +} + +type SharedDomain struct { + Id string `json:"id"` + Name string `json:"name"` +} + +type SharableResourceBaseInfo struct { + // 共享的项目列表 + SharedProjects []SharedProject `json:"shared_projects"` + // 共享的域列表 + SharedDomains []SharedDomain `json:"shared_domains"` } type SharableVirtualResourceDetails struct { VirtualResourceDetails - - SharedProjects []SharedProject `json:"shared_projects"` + SharableResourceBaseInfo } type AdminSharableVirtualResourceDetails struct { @@ -129,3 +143,16 @@ type ProjectizedResourceInfo struct { type ScopedResourceBaseInfo struct { ProjectizedResourceInfo } + +type InfrasResourceBaseDetails struct { + DomainLevelResourceDetails + SharableResourceBaseInfo +} + +type StatusInfrasResourceBaseDetails struct { + InfrasResourceBaseDetails +} + +type EnabledStatusInfrasResourceBaseDetails struct { + StatusInfrasResourceBaseDetails +} diff --git a/pkg/apis/update.go b/pkg/apis/update.go new file mode 100644 index 0000000000..94a3afdd1e --- /dev/null +++ b/pkg/apis/update.go @@ -0,0 +1,87 @@ +// 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 apis + +type ModelBaseUpdateInput struct { + Meta +} + +type ResourceBaseUpdateInput struct { + ModelBaseUpdateInput +} + +type StandaloneResourceBaseUpdateInput struct { + ResourceBaseUpdateInput + + // 资源名称 + Name string `json:"name"` + // 资源描述 + Description string `json:"description"` +} + +type StatusStandaloneResourceBaseUpdateInput struct { + StandaloneResourceBaseUpdateInput +} + +type EnabledStatusStandaloneResourceBaseUpdateInput struct { + StatusStandaloneResourceBaseUpdateInput +} + +type VirtualResourceBaseUpdateInput struct { + StatusStandaloneResourceBaseUpdateInput +} + +type SharableVirtualResourceBaseUpdateInput struct { + VirtualResourceBaseUpdateInput +} + +type AdminSharableVirtualResourceBaseUpdateInput struct { + SharableVirtualResourceBaseUpdateInput + + // 管理记录 + Records string `json:"records"` +} + +type DomainLevelResourceBaseUpdateInput struct { + StandaloneResourceBaseUpdateInput +} + +type StatusDomainLevelResourceBaseUpdateInput struct { + DomainLevelResourceBaseUpdateInput +} + +type EnabledStatusDomainLevelResourceBaseUpdateInput struct { + StatusDomainLevelResourceBaseUpdateInput +} + +type JointResourceBaseUpdateInput struct { + ResourceBaseUpdateInput +} + +type VirtualJointResourceBaseUpdateInput struct { + JointResourceBaseUpdateInput +} + +type InfrasResourceBaseUpdateInput struct { + DomainLevelResourceBaseUpdateInput +} + +type StatusInfrasResourceBaseUpdateInput struct { + InfrasResourceBaseUpdateInput +} + +type EnabledStatusInfrasResourceBaseUpdateInput struct { + StatusInfrasResourceBaseUpdateInput +} diff --git a/pkg/apis/yunionconf/doc.go b/pkg/apis/yunionconf/doc.go index ddc1f2711e..802129f456 100644 --- a/pkg/apis/yunionconf/doc.go +++ b/pkg/apis/yunionconf/doc.go @@ -1 +1,15 @@ +// 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 yunionconf // import "yunion.io/x/onecloud/pkg/apis/yunionconf" diff --git a/pkg/cloudcommon/consts/consts.go b/pkg/cloudcommon/consts/consts.go index 0a2396e460..8cd2203500 100644 --- a/pkg/cloudcommon/consts/consts.go +++ b/pkg/cloudcommon/consts/consts.go @@ -31,6 +31,8 @@ var ( defaultPagingLimit int64 = 2048 maxPagingLimit int64 = 2048 + + domainizedNamespace = true ) func SetRegion(region string) { @@ -72,3 +74,11 @@ func GetDefaultPagingLimit() int64 { func GetMaxPagingLimit() int64 { return maxPagingLimit } + +func SetDomainizedNamespace(domainNS bool) { + domainizedNamespace = domainNS +} + +func IsDomainizedNamespace() bool { + return nonDefaultDomainProjects && domainizedNamespace +} diff --git a/pkg/cloudcommon/db/adminsharablevirtual.go b/pkg/cloudcommon/db/adminsharablevirtual.go index d0dd78c306..eb3a0e0a68 100644 --- a/pkg/cloudcommon/db/adminsharablevirtual.go +++ b/pkg/cloudcommon/db/adminsharablevirtual.go @@ -31,7 +31,7 @@ import ( type SAdminSharableVirtualResourceBase struct { SSharableVirtualResourceBase - Records string `charset:"ascii" list:"user" create:"optional" update:"user"` + Records string `charset:"utf8" list:"user" create:"optional" update:"user"` } type SAdminSharableVirtualResourceBaseManager struct { @@ -226,3 +226,17 @@ func (manager *SAdminSharableVirtualResourceBaseManager) FetchCustomizeColumns( } return rows } + +func (model *SAdminSharableVirtualResourceBase) ValidateUpdateData( + ctx context.Context, + userCred mcclient.TokenCredential, + query jsonutils.JSONObject, + input apis.AdminSharableVirtualResourceBaseUpdateInput, +) (apis.AdminSharableVirtualResourceBaseUpdateInput, error) { + var err error + input.SharableVirtualResourceBaseUpdateInput, err = model.SSharableVirtualResourceBase.ValidateUpdateData(ctx, userCred, query, input.SharableVirtualResourceBaseUpdateInput) + if err != nil { + return input, errors.Wrap(err, "SSharableVirtualResourceBase.ValidateUpdateData") + } + return input, nil +} diff --git a/pkg/cloudcommon/db/caller.go b/pkg/cloudcommon/db/caller.go index 3e1bc8ab0d..d3197a8375 100644 --- a/pkg/cloudcommon/db/caller.go +++ b/pkg/cloudcommon/db/caller.go @@ -20,6 +20,8 @@ import ( "reflect" "yunion.io/x/jsonutils" + "yunion.io/x/log" + "yunion.io/x/pkg/errors" "yunion.io/x/pkg/gotypes" "yunion.io/x/sqlchemy" @@ -56,6 +58,50 @@ func call(obj interface{}, fName string, inputs ...interface{}) ([]reflect.Value return callObject(reflect.ValueOf(obj), fName, inputs...) } +func findFunc(modelVal reflect.Value, fName string) (reflect.Value, error) { + funcVal := modelVal.MethodByName(fName) + if !funcVal.IsValid() || funcVal.IsNil() { + log.Debugf("find method %s for %s", fName, modelVal.Type()) + if modelVal.Kind() != reflect.Ptr { + return funcVal, errors.Wrapf(httperrors.ErrNotImplemented, "%s not implemented", fName) + } + modelVal = modelVal.Elem() + if modelVal.Kind() != reflect.Struct { + return funcVal, errors.Wrapf(httperrors.ErrNotImplemented, "%s not implemented", fName) + } + modelType := modelVal.Type() + for i := 0; i < modelType.NumField(); i += 1 { + fieldType := modelType.Field(i) + if fieldType.Anonymous { + fieldValue := modelVal.Field(i) + if fieldValue.Kind() != reflect.Ptr && fieldValue.CanAddr() { + newFuncVal, err := findFunc(fieldValue.Addr(), fName) + if err == nil { + if !funcVal.IsValid() || funcVal.IsNil() { + funcVal = newFuncVal + } else { + return funcVal, errors.Wrapf(httperrors.ErrConflict, "%s is ambiguous", fName) + } + } + } else if fieldValue.Kind() == reflect.Ptr { + newFuncVal, err := findFunc(fieldValue, fName) + if err == nil { + if !funcVal.IsValid() || funcVal.IsNil() { + funcVal = newFuncVal + } else { + return funcVal, errors.Wrapf(httperrors.ErrConflict, "%s is ambiguous", fName) + } + } + } + } + } + if !funcVal.IsValid() || funcVal.IsNil() { + return funcVal, errors.Wrapf(httperrors.ErrNotImplemented, "%s is not implemented", fName) + } + } + return funcVal, nil +} + func callObject(modelVal reflect.Value, fName string, inputs ...interface{}) ([]reflect.Value, error) { funcVal := modelVal.MethodByName(fName) return callFunc(funcVal, fName, inputs...) diff --git a/pkg/cloudcommon/db/caller_test.go b/pkg/cloudcommon/db/caller_test.go index 6c7784600e..77ed1ef7d7 100644 --- a/pkg/cloudcommon/db/caller_test.go +++ b/pkg/cloudcommon/db/caller_test.go @@ -16,6 +16,7 @@ package db import ( "context" + "fmt" "reflect" "testing" @@ -117,3 +118,83 @@ func Test_call(t *testing.T) { }) } } + +type Embeded struct { +} + +func (e *Embeded) Method() { + fmt.Println("Embeded Method") +} + +type Struct0 struct { + Embeded +} + +type Struct1 struct { + Embeded +} + +type Struct2 struct { +} + +func (e *Struct0) Method() { + fmt.Println("Struct0 Method") +} + +type Top0 struct { + Struct0 + Struct1 + Struct2 +} + +type Top1 struct { + Struct0 + Struct1 + Struct2 +} + +func (e *Top1) Method() { + fmt.Println("Top1 Method") +} + +func TestFindFunc(t *testing.T) { + cases := []struct { + obj interface{} + want bool + }{ + { + obj: &Embeded{}, + want: true, + }, + { + obj: &Struct0{}, + want: true, + }, + { + obj: &Struct1{}, + want: true, + }, + { + obj: &Struct2{}, + want: false, + }, + { + obj: &Top0{}, + want: true, + }, + { + obj: &Top1{}, + want: true, + }, + } + for _, c := range cases { + t.Logf("%s is called", reflect.TypeOf(c.obj)) + funcVal, err := findFunc(reflect.ValueOf(c.obj), "Method") + if funcVal.IsValid() { + funcVal.Call(nil) + } + if (c.want && err != nil) || (!c.want && err == nil) { + t.Errorf("%s want %v but err==nil %v", reflect.TypeOf(c.obj), c.want, err == nil) + } + } +} diff --git a/pkg/cloudcommon/db/db_dispatcher.go b/pkg/cloudcommon/db/db_dispatcher.go index 457bdec33f..0ffca5d683 100644 --- a/pkg/cloudcommon/db/db_dispatcher.go +++ b/pkg/cloudcommon/db/db_dispatcher.go @@ -1570,14 +1570,6 @@ func updateItem(manager IModelManager, item IModel, ctx context.Context, userCre return nil, httperrors.NewInternalServerError("Invalid data JSONObject") } - name, _ := data.GetString("name") - if len(name) > 0 { - err = alterNameValidator(item, name) - if err != nil { - return nil, err - } - } - dataDict, err = ValidateUpdateData(item, ctx, userCred, query, dataDict) if err != nil { errMsg := fmt.Sprintf("validate update data error: %s", err) diff --git a/pkg/cloudcommon/db/domain.go b/pkg/cloudcommon/db/domain.go index c0dbbf32f1..bde30f3dd1 100644 --- a/pkg/cloudcommon/db/domain.go +++ b/pkg/cloudcommon/db/domain.go @@ -38,6 +38,14 @@ type SDomainizedResourceBase struct { DomainId string `width:"64" charset:"ascii" default:"default" nullable:"false" index:"true" list:"user" json:"domain_id"` } +func (manager *SDomainizedResourceBaseManager) NamespaceScope() rbacutils.TRbacScope { + if consts.IsDomainizedNamespace() { + return rbacutils.ScopeDomain + } else { + return rbacutils.ScopeSystem + } +} + func (manager *SDomainizedResourceBaseManager) ResourceScope() rbacutils.TRbacScope { return rbacutils.ScopeDomain } diff --git a/pkg/cloudcommon/db/domainresource.go b/pkg/cloudcommon/db/domainresource.go index 8e040bb313..4f6630a7f2 100644 --- a/pkg/cloudcommon/db/domainresource.go +++ b/pkg/cloudcommon/db/domainresource.go @@ -151,6 +151,18 @@ func (model *SDomainLevelResourceBase) PerformChangeOwner(ctx context.Context, u }) return nil, nil } + + // change domain, do check + if managed, ok := model.GetIDomainLevelModel().(IManagedResoucceBase); ok { + if !managed.CanShareToDomain(ownerId.GetProjectDomainId()) { + return nil, errors.Wrap(httperrors.ErrForbidden, "cann't share across domain") + } + } + + if !IsAdminAllowPerform(userCred, model, "change-owner") { + return nil, errors.Wrap(httperrors.ErrNotSufficientPrivilege, "require system privileges") + } + q := manager.Query().Equals("name", model.GetName()) q = manager.FilterByOwner(q, ownerId, manager.NamespaceScope()) q = manager.FilterBySystemAttributes(q, nil, nil, manager.ResourceScope()) @@ -169,10 +181,12 @@ func (model *SDomainLevelResourceBase) PerformChangeOwner(ctx context.Context, u former = &formerObj } - // clean shared projects before update project id - // if err := SharedResourceManager.CleanModelSharedProjects(ctx, userCred, model); err != nil { - // return nil, err - // } + // clean shared projects before update domain id + if sharedModel, ok := model.GetIDomainLevelModel().(ISharableBaseModel); ok { + if err := SharedResourceManager.CleanModelShares(ctx, userCred, sharedModel); err != nil { + return nil, err + } + } _, err = Update(model, func() error { model.DomainId = ownerId.GetProjectDomainId() @@ -271,3 +285,17 @@ func (model *SDomainLevelResourceBase) SyncCloudDomainId(userCred mcclient.Token func (model *SDomainLevelResourceBase) GetIDomainLevelModel() IDomainLevelModel { return model.GetVirtualObject().(IDomainLevelModel) } + +func (model *SDomainLevelResourceBase) ValidateUpdateData( + ctx context.Context, + userCred mcclient.TokenCredential, + query jsonutils.JSONObject, + input apis.DomainLevelResourceBaseUpdateInput, +) (apis.DomainLevelResourceBaseUpdateInput, error) { + var err error + input.StandaloneResourceBaseUpdateInput, err = model.SStandaloneResourceBase.ValidateUpdateData(ctx, userCred, query, input.StandaloneResourceBaseUpdateInput) + if err != nil { + return input, errors.Wrap(err, "SStandaloneResourceBase.ValidateUpdateData") + } + return input, nil +} diff --git a/pkg/cloudcommon/db/enabledstatusdomainresource.go b/pkg/cloudcommon/db/enabledstatusdomainresource.go index b6fc814edf..cd21d29580 100644 --- a/pkg/cloudcommon/db/enabledstatusdomainresource.go +++ b/pkg/cloudcommon/db/enabledstatusdomainresource.go @@ -146,3 +146,17 @@ func (model *SEnabledStatusDomainLevelResourceBase) GetExtraDetails( ) (apis.EnabledStatusDomainLevelResourceDetails, error) { return apis.EnabledStatusDomainLevelResourceDetails{}, nil } + +func (model *SEnabledStatusDomainLevelResourceBase) ValidateUpdateData( + ctx context.Context, + userCred mcclient.TokenCredential, + query jsonutils.JSONObject, + input apis.EnabledStatusDomainLevelResourceBaseUpdateInput, +) (apis.EnabledStatusDomainLevelResourceBaseUpdateInput, error) { + var err error + input.StatusDomainLevelResourceBaseUpdateInput, err = model.SStatusDomainLevelResourceBase.ValidateUpdateData(ctx, userCred, query, input.StatusDomainLevelResourceBaseUpdateInput) + if err != nil { + return input, errors.Wrap(err, "SStatusDomainLevelResourceBase.ValidateUpdateData") + } + return input, nil +} diff --git a/pkg/cloudcommon/db/enabledstatusinfrasresource.go b/pkg/cloudcommon/db/enabledstatusinfrasresource.go new file mode 100644 index 0000000000..d6a4271a30 --- /dev/null +++ b/pkg/cloudcommon/db/enabledstatusinfrasresource.go @@ -0,0 +1,162 @@ +// 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 db + +import ( + "context" + + "yunion.io/x/jsonutils" + "yunion.io/x/pkg/errors" + "yunion.io/x/sqlchemy" + + "yunion.io/x/onecloud/pkg/apis" + "yunion.io/x/onecloud/pkg/httperrors" + "yunion.io/x/onecloud/pkg/mcclient" + "yunion.io/x/onecloud/pkg/util/stringutils2" +) + +type SEnabledStatusInfrasResourceBase struct { + SStatusInfrasResourceBase + SEnabledResourceBase +} + +type SEnabledStatusInfrasResourceBaseManager struct { + SStatusInfrasResourceBaseManager + SEnabledResourceBaseManager +} + +func NewEnabledStatusInfrasResourceBaseManager(dt interface{}, tableName string, keyword string, keywordPlural string) SEnabledStatusInfrasResourceBaseManager { + return SEnabledStatusInfrasResourceBaseManager{ + SStatusInfrasResourceBaseManager: NewStatusInfrasResourceBaseManager(dt, tableName, keyword, keywordPlural), + } +} + +func (self *SEnabledStatusInfrasResourceBase) AllowPerformEnable(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, input apis.PerformEnableInput) bool { + return IsDomainAllowPerform(userCred, self, "enable") +} + +func (self *SEnabledStatusInfrasResourceBase) PerformEnable(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, input apis.PerformEnableInput) (jsonutils.JSONObject, error) { + err := EnabledPerformEnable(self, ctx, userCred, true) + if err != nil { + return nil, errors.Wrap(err, "EnabledPerformEnable") + } + return nil, nil +} + +func (self *SEnabledStatusInfrasResourceBase) AllowPerformDisable(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, input apis.PerformDisableInput) bool { + return IsDomainAllowPerform(userCred, self, "disable") +} + +func (self *SEnabledStatusInfrasResourceBase) PerformDisable(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, input apis.PerformDisableInput) (jsonutils.JSONObject, error) { + err := EnabledPerformEnable(self, ctx, userCred, false) + if err != nil { + return nil, errors.Wrap(err, "EnabledPerformEnable") + } + return nil, nil +} + +func (manager *SEnabledStatusInfrasResourceBaseManager) ValidateCreateData( + ctx context.Context, + userCred mcclient.TokenCredential, + ownerId mcclient.IIdentityProvider, + query jsonutils.JSONObject, + input apis.EnabledStatusInfrasResourceBaseCreateInput, +) (apis.EnabledStatusInfrasResourceBaseCreateInput, error) { + var err error + input.StatusInfrasResourceBaseCreateInput, err = manager.SStatusInfrasResourceBaseManager.ValidateCreateData(ctx, userCred, ownerId, query, input.StatusInfrasResourceBaseCreateInput) + if err != nil { + return input, errors.Wrap(err, "SStatusInfrasResourceBaseManager.ValidateCreateData") + } + return input, nil +} + +func (manager *SEnabledStatusInfrasResourceBaseManager) ListItemFilter( + ctx context.Context, + q *sqlchemy.SQuery, + userCred mcclient.TokenCredential, + query apis.EnabledStatusInfrasResourceBaseListInput, +) (*sqlchemy.SQuery, error) { + q, err := manager.SStatusInfrasResourceBaseManager.ListItemFilter(ctx, q, userCred, query.StatusInfrasResourceBaseListInput) + if err != nil { + return nil, errors.Wrap(err, "SStatusInfrasResourceBaseManager.ListItemFilter") + } + q, err = manager.SEnabledResourceBaseManager.ListItemFilter(ctx, q, userCred, query.EnabledResourceBaseListInput) + if err != nil { + return nil, errors.Wrap(err, "SEnabledResourceBaseManager.ListItemFilter") + } + return q, nil +} + +func (manager *SEnabledStatusInfrasResourceBaseManager) QueryDistinctExtraField(q *sqlchemy.SQuery, field string) (*sqlchemy.SQuery, error) { + q, err := manager.SStatusInfrasResourceBaseManager.QueryDistinctExtraField(q, field) + if err == nil { + return q, nil + } + return q, httperrors.ErrNotFound +} + +func (manager *SEnabledStatusInfrasResourceBaseManager) OrderByExtraFields( + ctx context.Context, + q *sqlchemy.SQuery, + userCred mcclient.TokenCredential, + query apis.EnabledStatusInfrasResourceBaseListInput, +) (*sqlchemy.SQuery, error) { + q, err := manager.SStatusInfrasResourceBaseManager.OrderByExtraFields(ctx, q, userCred, query.StatusInfrasResourceBaseListInput) + if err != nil { + return nil, errors.Wrap(err, "SStatusInfrasResourceBaseManager.OrderByExtraFields") + } + return q, nil +} + +func (manager *SEnabledStatusInfrasResourceBaseManager) FetchCustomizeColumns( + ctx context.Context, + userCred mcclient.TokenCredential, + query jsonutils.JSONObject, + objs []interface{}, + fields stringutils2.SSortedStrings, + isList bool, +) []apis.EnabledStatusInfrasResourceBaseDetails { + rows := make([]apis.EnabledStatusInfrasResourceBaseDetails, len(objs)) + domainRows := manager.SStatusInfrasResourceBaseManager.FetchCustomizeColumns(ctx, userCred, query, objs, fields, isList) + for i := range rows { + rows[i] = apis.EnabledStatusInfrasResourceBaseDetails{ + StatusInfrasResourceBaseDetails: domainRows[i], + } + } + return rows +} + +func (model *SEnabledStatusInfrasResourceBase) GetExtraDetails( + ctx context.Context, + userCred mcclient.TokenCredential, + query jsonutils.JSONObject, + isList bool, +) (apis.EnabledStatusInfrasResourceBaseDetails, error) { + return apis.EnabledStatusInfrasResourceBaseDetails{}, nil +} + +func (model *SEnabledStatusInfrasResourceBase) ValidateUpdateData( + ctx context.Context, + userCred mcclient.TokenCredential, + query jsonutils.JSONObject, + input apis.EnabledStatusInfrasResourceBaseUpdateInput, +) (apis.EnabledStatusInfrasResourceBaseUpdateInput, error) { + var err error + input.StatusInfrasResourceBaseUpdateInput, err = model.SStatusInfrasResourceBase.ValidateUpdateData(ctx, userCred, query, input.StatusInfrasResourceBaseUpdateInput) + if err != nil { + return input, errors.Wrap(err, "SStatusInfrasResourceBase.ValidateUpdateData") + } + return input, nil +} diff --git a/pkg/cloudcommon/db/enabledstatusstandalone.go b/pkg/cloudcommon/db/enabledstatusstandalone.go index f598cd6b7b..82c3ca3763 100644 --- a/pkg/cloudcommon/db/enabledstatusstandalone.go +++ b/pkg/cloudcommon/db/enabledstatusstandalone.go @@ -140,3 +140,17 @@ func (model *SEnabledStatusStandaloneResourceBase) GetExtraDetails( ) (apis.EnabledStatusStandaloneResourceDetails, error) { return apis.EnabledStatusStandaloneResourceDetails{}, nil } + +func (model *SEnabledStatusStandaloneResourceBase) ValidateUpdateData( + ctx context.Context, + userCred mcclient.TokenCredential, + query jsonutils.JSONObject, + input apis.EnabledStatusStandaloneResourceBaseUpdateInput, +) (apis.EnabledStatusStandaloneResourceBaseUpdateInput, error) { + var err error + input.StatusStandaloneResourceBaseUpdateInput, err = model.SStatusStandaloneResourceBase.ValidateUpdateData(ctx, userCred, query, input.StatusStandaloneResourceBaseUpdateInput) + if err != nil { + return input, errors.Wrap(err, "SStatusStandaloneResourceBase.ValidateUpdateData") + } + return input, nil +} diff --git a/pkg/cloudcommon/db/infraresource.go b/pkg/cloudcommon/db/infraresource.go new file mode 100644 index 0000000000..562509c972 --- /dev/null +++ b/pkg/cloudcommon/db/infraresource.go @@ -0,0 +1,203 @@ +// 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 db + +import ( + "context" + + "yunion.io/x/jsonutils" + "yunion.io/x/pkg/errors" + "yunion.io/x/sqlchemy" + + "yunion.io/x/onecloud/pkg/apis" + "yunion.io/x/onecloud/pkg/httperrors" + "yunion.io/x/onecloud/pkg/mcclient" + "yunion.io/x/onecloud/pkg/util/rbacutils" + "yunion.io/x/onecloud/pkg/util/stringutils2" +) + +type SInfrasResourceBaseManager struct { + SDomainLevelResourceBaseManager + SSharableBaseResourceManager +} + +func NewInfrasResourceBaseManager( + dt interface{}, + tableName string, + keyword string, + keywordPlural string, +) SInfrasResourceBaseManager { + return SInfrasResourceBaseManager{ + SDomainLevelResourceBaseManager: NewDomainLevelResourceBaseManager(dt, tableName, keyword, keywordPlural), + } +} + +type SInfrasResourceBase struct { + SDomainLevelResourceBase + SSharableBaseResource +} + +func (manager *SInfrasResourceBaseManager) GetIInfrasModelManager() IInfrasModelManager { + return manager.GetVirtualObject().(IInfrasModelManager) +} + +func (manager *SInfrasResourceBaseManager) FilterByOwner(q *sqlchemy.SQuery, owner mcclient.IIdentityProvider, scope rbacutils.TRbacScope) *sqlchemy.SQuery { + return SharableManagerFilterByOwner(manager.GetIInfrasModelManager(), q, owner, scope) +} + +func (model *SInfrasResourceBase) AllowGetDetails(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject) bool { + return ((model.IsOwner(userCred) || model.IsSharable(userCred)) && IsAllowGet(rbacutils.ScopeDomain, userCred, model)) || IsAllowGet(rbacutils.ScopeSystem, userCred, model) +} + +func (model *SInfrasResourceBase) IsSharable(reqUsrId mcclient.IIdentityProvider) bool { + return SharableModelIsSharable(model.GetIInfrasModel(), reqUsrId) +} + +func (model *SInfrasResourceBase) IsShared() bool { + return SharableModelIsShared(model) +} + +func (model *SInfrasResourceBase) AllowPerformPublic(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, input apis.PerformPublicInput) bool { + return true +} + +func (model *SInfrasResourceBase) PerformPublic(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, input apis.PerformPublicInput) (jsonutils.JSONObject, error) { + err := SharablePerformPublic(model, ctx, userCred, input) + if err != nil { + return nil, errors.Wrap(err, "SharablePerformPublic") + } + return nil, nil +} + +func (model *SInfrasResourceBase) AllowPerformPrivate(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, input apis.PerformPrivateInput) bool { + return true +} + +func (model *SInfrasResourceBase) PerformPrivate(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, input apis.PerformPrivateInput) (jsonutils.JSONObject, error) { + err := SharablePerformPrivate(model, ctx, userCred) + if err != nil { + return nil, errors.Wrap(err, "SharablePerformPrivate") + } + return nil, nil +} + +func (model *SInfrasResourceBase) GetIInfrasModel() IInfrasModel { + return model.GetVirtualObject().(IInfrasModel) +} + +func (manager *SInfrasResourceBaseManager) ValidateCreateData( + ctx context.Context, + userCred mcclient.TokenCredential, + ownerId mcclient.IIdentityProvider, + query jsonutils.JSONObject, + input apis.InfrasResourceBaseCreateInput, +) (apis.InfrasResourceBaseCreateInput, error) { + var err error + input.DomainLevelResourceCreateInput, err = manager.SDomainLevelResourceBaseManager.ValidateCreateData(ctx, userCred, ownerId, query, input.DomainLevelResourceCreateInput) + if err != nil { + return input, errors.Wrap(err, "manager.SDomainLevelResourceBaseManager.ValidateCreateData") + } + return input, nil +} + +func (manager *SInfrasResourceBaseManager) ListItemFilter( + ctx context.Context, + q *sqlchemy.SQuery, + userCred mcclient.TokenCredential, + query apis.InfrasResourceBaseListInput, +) (*sqlchemy.SQuery, error) { + q, err := manager.SDomainLevelResourceBaseManager.ListItemFilter(ctx, q, userCred, query.DomainLevelResourceListInput) + if err != nil { + return nil, errors.Wrap(err, "SDomainLevelResourceBaseManager.ListItemFilter") + } + q, err = manager.SSharableBaseResourceManager.ListItemFilter(ctx, q, userCred, query.SharableResourceBaseListInput) + if err != nil { + return nil, errors.Wrap(err, "SSharableBaseResourceManager.ListItemFilter") + } + return q, nil +} + +func (manager *SInfrasResourceBaseManager) QueryDistinctExtraField(q *sqlchemy.SQuery, field string) (*sqlchemy.SQuery, error) { + q, err := manager.SDomainLevelResourceBaseManager.QueryDistinctExtraField(q, field) + if err == nil { + return q, nil + } + return q, httperrors.ErrNotFound +} + +func (manager *SInfrasResourceBaseManager) OrderByExtraFields(ctx context.Context, q *sqlchemy.SQuery, userCred mcclient.TokenCredential, query apis.InfrasResourceBaseListInput) (*sqlchemy.SQuery, error) { + q, err := manager.SDomainLevelResourceBaseManager.OrderByExtraFields(ctx, q, userCred, query.DomainLevelResourceListInput) + if err != nil { + return nil, errors.Wrap(err, "SDomainLevelResourceBaseManager.OrderByExtraFields") + } + return q, nil +} + +func (model *SInfrasResourceBase) GetExtraDetails(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, isList bool) (apis.InfrasResourceBaseDetails, error) { + return apis.InfrasResourceBaseDetails{}, nil +} + +func (manager *SInfrasResourceBaseManager) FetchCustomizeColumns( + ctx context.Context, + userCred mcclient.TokenCredential, + query jsonutils.JSONObject, + objs []interface{}, + fields stringutils2.SSortedStrings, + isList bool, +) []apis.InfrasResourceBaseDetails { + rows := make([]apis.InfrasResourceBaseDetails, len(objs)) + + domainRows := manager.SDomainLevelResourceBaseManager.FetchCustomizeColumns(ctx, userCred, query, objs, fields, isList) + shareRows := manager.SSharableBaseResourceManager.FetchCustomizeColumns(ctx, userCred, query, objs, fields, isList) + + for i := range rows { + rows[i] = apis.InfrasResourceBaseDetails{ + DomainLevelResourceDetails: domainRows[i], + SharableResourceBaseInfo: shareRows[i], + } + } + + return rows +} + +func (model *SInfrasResourceBase) ValidateUpdateData( + ctx context.Context, + userCred mcclient.TokenCredential, + query jsonutils.JSONObject, + input apis.InfrasResourceBaseUpdateInput, +) (apis.InfrasResourceBaseUpdateInput, error) { + var err error + input.DomainLevelResourceBaseUpdateInput, err = model.SDomainLevelResourceBase.ValidateUpdateData(ctx, userCred, query, input.DomainLevelResourceBaseUpdateInput) + if err != nil { + return input, errors.Wrap(err, "SDomainLevelResourceBase.ValidateUpdateData") + } + return input, nil +} + +func (model *SInfrasResourceBase) GetSharedDomains() []string { + return SharableGetSharedProjects(model, SharedTargetDomain) +} + +func (model *SInfrasResourceBase) PerformChangeOwner( + ctx context.Context, + userCred mcclient.TokenCredential, + query jsonutils.JSONObject, + input apis.PerformChangeDomainOwnerInput, +) (jsonutils.JSONObject, error) { + if model.IsShared() { + return nil, errors.Wrap(httperrors.ErrInvalidStatus, "cannot change owner when shared!") + } + return model.SDomainLevelResourceBase.PerformChangeOwner(ctx, userCred, query, input) +} diff --git a/pkg/cloudcommon/db/interface.go b/pkg/cloudcommon/db/interface.go index 05b713b78a..983d6017b3 100644 --- a/pkg/cloudcommon/db/interface.go +++ b/pkg/cloudcommon/db/interface.go @@ -276,13 +276,15 @@ type IStandaloneModel interface { RemoveMetadata(ctx context.Context, key string, userCred mcclient.TokenCredential) error RemoveAllMetadata(ctx context.Context, userCred mcclient.TokenCredential) error GetAllMetadata(userCred mcclient.TokenCredential) (map[string]string, error) + + IsShared() bool } type IDomainLevelModelManager interface { IStandaloneModelManager GetIDomainLevelModelManager() IDomainLevelModelManager - GetResourceCount() ([]SProjectResourceCount, error) + GetResourceCount() ([]SDomainResourceCount, error) } type IDomainLevelModel interface { @@ -295,6 +297,20 @@ type IDomainLevelModel interface { GetIDomainLevelModel() IDomainLevelModel } +type IInfrasModelManager interface { + IDomainLevelModelManager + + GetIInfrasModelManager() IInfrasModelManager +} + +type IInfrasModel interface { + IDomainLevelModel + ISharableBase + + GetIInfrasModel() IInfrasModel + GetSharedDomains() []string +} + type IVirtualModelManager interface { IStandaloneModelManager @@ -322,9 +338,11 @@ type ISharableVirtualModelManager interface { type ISharableVirtualModel interface { IVirtualModel + ISharableBase GetISharableVirtualModel() ISharableVirtualModel GetSharedProjects() []string + GetSharedDomains() []string } type IAdminSharableVirtualModelManager interface { diff --git a/pkg/cloudcommon/db/jointbase.go b/pkg/cloudcommon/db/jointbase.go index 282c46e527..e708220bee 100644 --- a/pkg/cloudcommon/db/jointbase.go +++ b/pkg/cloudcommon/db/jointbase.go @@ -282,3 +282,17 @@ func (manager *SJointResourceBaseManager) OrderByExtraFields( return q, nil } + +func (model *SJointResourceBase) ValidateUpdateData( + ctx context.Context, + userCred mcclient.TokenCredential, + query jsonutils.JSONObject, + input apis.JointResourceBaseUpdateInput, +) (apis.JointResourceBaseUpdateInput, error) { + var err error + input.ResourceBaseUpdateInput, err = model.SResourceBase.ValidateUpdateData(ctx, userCred, query, input.ResourceBaseUpdateInput) + if err != nil { + return input, errors.Wrap(err, "SResourceBase.ValidateUpdateData") + } + return input, nil +} diff --git a/pkg/cloudcommon/db/managed.go b/pkg/cloudcommon/db/managed.go new file mode 100644 index 0000000000..343c096273 --- /dev/null +++ b/pkg/cloudcommon/db/managed.go @@ -0,0 +1,19 @@ +// 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 db + +type IManagedResoucceBase interface { + CanShareToDomain(domainId string) bool +} diff --git a/pkg/cloudcommon/db/modelbase.go b/pkg/cloudcommon/db/modelbase.go index f00240b540..477457744c 100644 --- a/pkg/cloudcommon/db/modelbase.go +++ b/pkg/cloudcommon/db/modelbase.go @@ -508,8 +508,8 @@ func (model *SModelBase) AllowUpdateItem(ctx context.Context, userCred mcclient. return false } -func (model *SModelBase) ValidateUpdateData(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, data *jsonutils.JSONDict) (*jsonutils.JSONDict, error) { - return data, nil +func (model *SModelBase) ValidateUpdateData(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, input apis.ModelBaseUpdateInput) (apis.ModelBaseUpdateInput, error) { + return input, nil } func (model *SModelBase) PreUpdate(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, data jsonutils.JSONObject) { diff --git a/pkg/cloudcommon/db/opslog.go b/pkg/cloudcommon/db/opslog.go index 906e8e8a51..648102c2d1 100644 --- a/pkg/cloudcommon/db/opslog.go +++ b/pkg/cloudcommon/db/opslog.go @@ -49,6 +49,9 @@ const ( ACT_DETACH_FAIL = "detach_fail" ACT_DELETE_FAIL = "delete_fail" + ACT_PUBLIC = "public" + ACT_PRIVATE = "private" + ACT_SYNC_UPDATE = "sync_update" ACT_SYNC_CREATE = "sync_create" diff --git a/pkg/cloudcommon/db/resourcebase.go b/pkg/cloudcommon/db/resourcebase.go index 7c5b2e4b7e..3cb1d8104b 100644 --- a/pkg/cloudcommon/db/resourcebase.go +++ b/pkg/cloudcommon/db/resourcebase.go @@ -176,3 +176,12 @@ func (manager *SResourceBaseManager) FetchCustomizeColumns( func (model *SResourceBase) GetExtraDetails(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, isList bool) (apis.ResourceBaseDetails, error) { return apis.ResourceBaseDetails{}, nil } + +func (model *SResourceBase) ValidateUpdateData(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, input apis.ResourceBaseUpdateInput) (apis.ResourceBaseUpdateInput, error) { + var err error + input.ModelBaseUpdateInput, err = model.SModelBase.ValidateUpdateData(ctx, userCred, query, input.ModelBaseUpdateInput) + if err != nil { + return input, errors.Wrap(err, "SModelBase.ValidateUpdateData") + } + return input, nil +} diff --git a/pkg/cloudcommon/db/sharablebase.go b/pkg/cloudcommon/db/sharablebase.go index fb71e9e889..b1efde77a6 100644 --- a/pkg/cloudcommon/db/sharablebase.go +++ b/pkg/cloudcommon/db/sharablebase.go @@ -18,6 +18,8 @@ import ( "context" "yunion.io/x/jsonutils" + "yunion.io/x/log" + "yunion.io/x/pkg/errors" "yunion.io/x/sqlchemy" "yunion.io/x/onecloud/pkg/apis" @@ -26,105 +28,11 @@ import ( "yunion.io/x/onecloud/pkg/httperrors" "yunion.io/x/onecloud/pkg/mcclient" "yunion.io/x/onecloud/pkg/util/rbacutils" + "yunion.io/x/onecloud/pkg/util/stringutils2" ) type SSharableBaseResourceManager struct{} -func (manager *SSharableBaseResourceManager) FilterByOwner(q *sqlchemy.SQuery, owner mcclient.IIdentityProvider, scope rbacutils.TRbacScope) *sqlchemy.SQuery { - if owner != nil { - switch scope { - case rbacutils.ScopeProject, rbacutils.ScopeDomain: - if len(owner.GetProjectDomainId()) > 0 { - q = q.Filter(sqlchemy.OR( - sqlchemy.Equals(q.Field("domain_id"), owner.GetProjectDomainId()), - sqlchemy.IsTrue(q.Field("is_public")), - )) - } - } - } - return q -} - -type SSharableBaseResource struct { - IsPublic bool `default:"false" nullable:"false" list:"user"` -} - -type ISharableBase interface { - IModel - SetIsPublic(pub bool) - GetIsPublic() bool -} - -func (m *SSharableBaseResource) IsSharable(ownerId mcclient.IIdentityProvider) bool { - return m.IsPublic -} - -func (m *SSharableBaseResource) SetIsPublic(pub bool) { - m.IsPublic = pub -} - -func (m SSharableBaseResource) GetIsPublic() bool { - return m.IsPublic -} - -func SharableAllowPerformPublic(model ISharableBase, userCred mcclient.TokenCredential) bool { - return IsAllowPerform(rbacutils.ScopeSystem, userCred, model, "public") -} - -func SharablePerformPublic(model ISharableBase, ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, data jsonutils.JSONObject) (jsonutils.JSONObject, error) { - if model.GetIsPublic() { - return nil, nil - } - - scope := policy.PolicyManager.AllowScope(userCred, consts.GetServiceType(), model.GetModelManager().KeywordPlural(), policy.PolicyActionPerform, "public") - if scope != rbacutils.ScopeSystem { - return nil, httperrors.NewForbiddenError("not enough privilege") - } - - /*ownerId := model.GetOwnerId() - if userCred.GetProjectDomainId() != ownerId.GetProjectDomainId() { - return nil, httperrors.NewForbiddenError("not owner") - }*/ - - diff, err := Update(model, func() error { - model.SetIsPublic(true) - return nil - }) - if err == nil { - OpsLog.LogEvent(model, ACT_UPDATE, diff, userCred) - } - return nil, err -} - -func SharableAllowPerformPrivate(model ISharableBase, userCred mcclient.TokenCredential) bool { - return IsAllowPerform(rbacutils.ScopeSystem, userCred, model, "private") -} - -func SharablePerformPrivate(model ISharableBase, ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, data jsonutils.JSONObject) (jsonutils.JSONObject, error) { - if !model.GetIsPublic() { - return nil, nil - } - - scope := policy.PolicyManager.AllowScope(userCred, consts.GetServiceType(), model.GetModelManager().KeywordPlural(), policy.PolicyActionPerform, "private") - if scope != rbacutils.ScopeSystem { - return nil, httperrors.NewForbiddenError("not enough privilege") - } - - /*ownerId := model.GetOwnerId() - if userCred.GetProjectDomainId() != ownerId.GetProjectDomainId() { - return nil, httperrors.NewForbiddenError("not owner") - }*/ - - diff, err := Update(model, func() error { - model.SetIsPublic(false) - return nil - }) - if err == nil { - OpsLog.LogEvent(model, ACT_UPDATE, diff, userCred) - } - return nil, err -} - func (manager *SSharableBaseResourceManager) ListItemFilter( ctx context.Context, q *sqlchemy.SQuery, @@ -138,5 +46,337 @@ func (manager *SSharableBaseResourceManager) ListItemFilter( q = q.IsFalse("is_public") } } + if len(query.PublicScope) > 0 { + q = q.Equals("public_scope", query.PublicScope) + } return q, nil } + +func (manager *SSharableBaseResourceManager) FetchCustomizeColumns( + ctx context.Context, + userCred mcclient.TokenCredential, + query jsonutils.JSONObject, + objs []interface{}, + fields stringutils2.SSortedStrings, + isList bool, +) []apis.SharableResourceBaseInfo { + rows := make([]apis.SharableResourceBaseInfo, len(objs)) + + var resType string + resIds := make([]string, len(rows)) + var sharedTarget string + for i := range rows { + if model, ok := objs[i].(ISharableBaseModel); ok { + if len(resType) == 0 { + resType = model.Keyword() + } + if len(sharedTarget) == 0 { + sharedTarget = string(model.GetModelManager().ResourceScope()) + } + resIds[i] = model.GetId() + } + } + + q := SharedResourceManager.Query() + q = q.Equals("resource_type", resType) + q = q.Equals("target_type", sharedTarget) + + sharedResourceMap := make(map[string][]SSharedResource) + err := FetchQueryObjectsByIds(q, "resource_id", resIds, &sharedResourceMap) + if err != nil { + log.Errorf("FetchQueryObjectsByIds for shared resource fail %s", err) + return rows + } + + targetIds := stringutils2.NewSortedStrings([]string{}) + + for _, srs := range sharedResourceMap { + for _, sr := range srs { + targetIds = stringutils2.Append(targetIds, sr.TargetProjectId) + } + } + + if len(targetIds) == 0 { + return rows + } + + tenantMap := make(map[string]STenant) + var subq *sqlchemy.SQuery + switch sharedTarget { + case SharedTargetProject: + subq = TenantCacheManager.GetTenantQuery() + case SharedTargetDomain: + subq = TenantCacheManager.GetDomainQuery() + } + + err = FetchQueryObjectsByIds(subq, "id", targetIds, &tenantMap) + if err != nil { + log.Errorf("FetchQueryObjectsByIds for tenant_cache fail %s", err) + return rows + } + + for i := range rows { + resId := resIds[i] + if srs, ok := sharedResourceMap[resId]; ok { + switch sharedTarget { + case SharedTargetProject: + projects := make([]apis.SharedProject, len(srs)) + for si, sr := range srs { + projects[si].Id = sr.TargetProjectId + if tenant, ok := tenantMap[sr.TargetProjectId]; ok { + projects[si].Name = tenant.Name + projects[si].Domain = tenant.Domain + projects[si].DomainId = tenant.DomainId + } + } + rows[i].SharedProjects = projects + case SharedTargetDomain: + domains := make([]apis.SharedDomain, len(srs)) + for si, sr := range srs { + domains[si].Id = sr.TargetProjectId + if tenant, ok := tenantMap[sr.TargetProjectId]; ok { + domains[si].Name = tenant.Name + } + } + rows[i].SharedDomains = domains + } + } + } + + return rows +} + +func SharableManagerFilterByOwner(manager IStandaloneModelManager, q *sqlchemy.SQuery, owner mcclient.IIdentityProvider, scope rbacutils.TRbacScope) *sqlchemy.SQuery { + if owner != nil { + resScope := manager.ResourceScope() + if resScope == rbacutils.ScopeProject && scope == rbacutils.ScopeProject { + ownerProjectId := owner.GetProjectId() + if len(ownerProjectId) > 0 { + subq := SharedResourceManager.Query("resource_id") + subq = subq.Equals("resource_type", manager.Keyword()) + subq = subq.Equals("target_project_id", ownerProjectId) + subq = subq.Equals("target_type", SharedTargetProject) + q = q.Filter(sqlchemy.OR( + sqlchemy.Equals(q.Field("tenant_id"), ownerProjectId), + sqlchemy.AND( + sqlchemy.IsTrue(q.Field("is_public")), + sqlchemy.Equals(q.Field("public_scope"), rbacutils.ScopeSystem), + ), + sqlchemy.AND( + sqlchemy.IsTrue(q.Field("is_public")), + sqlchemy.Equals(q.Field("public_scope"), rbacutils.ScopeDomain), + sqlchemy.Equals(q.Field("domain_id"), owner.GetProjectDomainId()), + ), + sqlchemy.In(q.Field("id"), subq.SubQuery()), + )) + } + } else if (resScope == rbacutils.ScopeDomain && (scope == rbacutils.ScopeProject || scope == rbacutils.ScopeDomain)) || (resScope == rbacutils.ScopeProject && scope == rbacutils.ScopeDomain) { + ownerDomainId := owner.GetProjectDomainId() + if len(ownerDomainId) > 0 { + subq := SharedResourceManager.Query("resource_id") + subq = subq.Equals("resource_type", manager.Keyword()) + subq = subq.Equals("target_project_id", ownerDomainId) + subq = subq.Equals("target_type", SharedTargetDomain) + q = q.Filter(sqlchemy.OR( + sqlchemy.Equals(q.Field("domain_id"), ownerDomainId), + sqlchemy.AND( + sqlchemy.IsTrue(q.Field("is_public")), + sqlchemy.Equals(q.Field("public_scope"), rbacutils.ScopeSystem), + ), + sqlchemy.AND( + sqlchemy.IsTrue(q.Field("is_public")), + sqlchemy.In(q.Field("id"), subq.SubQuery()), + ), + )) + } + } + } + return q +} + +type SSharableBaseResource struct { + // 是否共享 + IsPublic bool `default:"false" nullable:"false" list:"user"` + // 默认共享范围 + PublicScope string `width:"16" charset:"ascii" nullable:"false" default:"system" list:"user"` +} + +type ISharableBaseModel interface { + IStandaloneModel + ISharableBase +} + +type ISharableBase interface { + SetShare(pub bool, scoe rbacutils.TRbacScope) + GetIsPublic() bool + GetPublicScope() rbacutils.TRbacScope +} + +func SharableModelIsSharable(model ISharableBaseModel, reqUsrId mcclient.IIdentityProvider) bool { + if model.GetIsPublic() && model.GetPublicScope() == rbacutils.ScopeSystem { + return true + } + ownerId := model.GetOwnerId() + if model.GetIsPublic() && model.GetPublicScope() == rbacutils.ScopeDomain { + if ownerId != nil && ownerId.GetProjectDomainId() == reqUsrId.GetProjectDomainId() { + return true + } + q := SharedResourceManager.Query().Equals("resource_id", model.GetId()) + q = q.Equals("resource_type", model.Keyword()) + q = q.Equals("target_project_id", reqUsrId.GetProjectDomainId()) + q = q.Equals("target_type", SharedTargetDomain) + cnt, _ := q.CountWithError() + if cnt > 0 { + return true + } + } + if model.GetPublicScope() == rbacutils.ScopeProject { + if ownerId != nil && ownerId.GetProjectId() == reqUsrId.GetProjectId() { + return true + } + q := SharedResourceManager.Query().Equals("resource_id", model.GetId()) + q = q.Equals("resource_type", model.Keyword()) + q = q.Equals("target_project_id", reqUsrId.GetProjectId()) + q = q.Equals("target_type", SharedTargetProject) + cnt, _ := q.CountWithError() + if cnt > 0 { + return true + } + } + return false +} + +func (m *SSharableBaseResource) SetShare(pub bool, scope rbacutils.TRbacScope) { + m.IsPublic = pub + m.PublicScope = string(scope) +} + +func (m SSharableBaseResource) GetIsPublic() bool { + return m.IsPublic +} + +func (m SSharableBaseResource) GetPublicScope() rbacutils.TRbacScope { + return rbacutils.String2Scope(m.PublicScope) +} + +func SharablePerformPublic(model ISharableBaseModel, ctx context.Context, userCred mcclient.TokenCredential, input apis.PerformPublicInput) error { + var err error + + resourceScope := model.GetModelManager().ResourceScope() + targetScope := rbacutils.String2ScopeDefault(input.Scope, rbacutils.ScopeSystem) + if resourceScope.HigherThan(targetScope) { + return errors.Wrapf(httperrors.ErrNotSupported, "cannot share %s resource to %s", resourceScope, targetScope) + } + + shareResult := apis.PerformPublicInput{ + Scope: string(targetScope), + } + + switch targetScope { + case rbacutils.ScopeProject: + if len(input.SharedProjects) == 0 { + return errors.Wrap(httperrors.ErrEmptyRequest, "empty shared target project list") + } + shareResult.SharedProjects, err = SharedResourceManager.shareToTarget(ctx, userCred, model, SharedTargetProject, input.SharedProjects) + if err != nil { + return errors.Wrap(err, "shareToTarget") + } + case rbacutils.ScopeDomain: + _, err = SharedResourceManager.shareToTarget(ctx, userCred, model, SharedTargetProject, nil) + if err != nil { + return errors.Wrap(err, "shareToTarget clean projects") + } + shareResult.SharedDomains, err = SharedResourceManager.shareToTarget(ctx, userCred, model, SharedTargetDomain, input.SharedDomains) + if err != nil { + return errors.Wrap(err, "shareToTarget add domains") + } + case rbacutils.ScopeSystem: + _, err = SharedResourceManager.shareToTarget(ctx, userCred, model, SharedTargetProject, nil) + if err != nil { + return errors.Wrap(err, "shareToTarget clean projects") + } + _, err = SharedResourceManager.shareToTarget(ctx, userCred, model, SharedTargetDomain, nil) + if err != nil { + return errors.Wrap(err, "shareToTarget clean domainss") + } + } + + allowScope := policy.PolicyManager.AllowScope(userCred, consts.GetServiceType(), model.KeywordPlural(), policy.PolicyActionPerform, "public") + if targetScope.HigherThan(allowScope) { + return errors.Wrapf(httperrors.ErrNotSufficientPrivilege, "require %s allow %s", targetScope, allowScope) + } + + _, err = Update(model, func() error { + model.SetShare(true, targetScope) + return nil + }) + + if err != nil { + return errors.Wrap(err, "Update") + } + + OpsLog.LogEvent(model, ACT_PUBLIC, shareResult, userCred) + + model.GetIStandaloneModel().ClearSchedDescCache() + return nil +} + +func SharablePerformPrivate(model ISharableBaseModel, ctx context.Context, userCred mcclient.TokenCredential) error { + if !model.GetIsPublic() { + return nil + } + + requireScope := model.GetPublicScope() + allowScope := policy.PolicyManager.AllowScope(userCred, consts.GetServiceType(), model.GetModelManager().KeywordPlural(), policy.PolicyActionPerform, "private") + if requireScope.HigherThan(allowScope) { + return errors.Wrapf(httperrors.ErrNotSufficientPrivilege, "require %s allow %s", requireScope, allowScope) + } + + err := SharedResourceManager.CleanModelShares(ctx, userCred, model) + if err != nil { + return errors.Wrap(err, "CleanModelShares") + } + + diff, err := Update(model, func() error { + model.SetShare(false, rbacutils.ScopeNone) + return nil + }) + + if err != nil { + return errors.Wrap(err, "Update") + } + + OpsLog.LogEvent(model, ACT_PRIVATE, diff, userCred) + + model.GetIStandaloneModel().ClearSchedDescCache() + + return nil +} + +func SharableGetSharedProjects(model ISharableBaseModel, targetType string) []string { + sharedResources := make([]SSharedResource, 0) + q := SharedResourceManager.Query() + q = q.Equals("resource_type", model.Keyword()) + q = q.Equals("resource_id", model.GetId()) + q = q.Equals("target_type", targetType) + err := q.All(&sharedResources) + if err != nil { + return nil + } + res := make([]string, len(sharedResources)) + for i := range sharedResources { + res[i] = sharedResources[i].TargetProjectId + } + return res +} + +func SharableModelIsShared(model ISharableBaseModel) bool { + q := SharedResourceManager.Query() + q = q.Equals("resource_type", model.Keyword()) + q = q.Equals("resource_id", model.GetId()) + cnt, _ := q.CountWithError() + if cnt > 0 { + return true + } + return false +} diff --git a/pkg/cloudcommon/db/sharablevirtual.go b/pkg/cloudcommon/db/sharablevirtual.go index 8451837a5b..bbef69ea2c 100644 --- a/pkg/cloudcommon/db/sharablevirtual.go +++ b/pkg/cloudcommon/db/sharablevirtual.go @@ -18,14 +18,10 @@ import ( "context" "yunion.io/x/jsonutils" - "yunion.io/x/log" "yunion.io/x/pkg/errors" - "yunion.io/x/pkg/utils" "yunion.io/x/sqlchemy" "yunion.io/x/onecloud/pkg/apis" - "yunion.io/x/onecloud/pkg/cloudcommon/consts" - "yunion.io/x/onecloud/pkg/cloudcommon/policy" "yunion.io/x/onecloud/pkg/httperrors" "yunion.io/x/onecloud/pkg/mcclient" "yunion.io/x/onecloud/pkg/util/rbacutils" @@ -34,13 +30,14 @@ import ( type SSharableVirtualResourceBase struct { SVirtualResourceBase - - IsPublic bool `default:"false" nullable:"false" create:"domain_optional" list:"user" json:"is_public"` - PublicScope string `width:"16" charset:"ascii" nullable:"false" default:"system" create:"domain_optional" list:"user" json:"public_scope"` + SSharableBaseResource + // IsPublic bool `default:"false" nullable:"false" create:"domain_optional" list:"user" json:"is_public"` + // PublicScope string `width:"16" charset:"ascii" nullable:"false" default:"system" create:"domain_optional" list:"user" json:"public_scope"` } type SSharableVirtualResourceBaseManager struct { SVirtualResourceBaseManager + SSharableBaseResourceManager } func NewSharableVirtualResourceBaseManager(dt interface{}, tableName string, keyword string, keywordPlural string) SSharableVirtualResourceBaseManager { @@ -52,196 +49,42 @@ func (manager *SSharableVirtualResourceBaseManager) GetISharableVirtualModelMana } func (manager *SSharableVirtualResourceBaseManager) FilterByOwner(q *sqlchemy.SQuery, owner mcclient.IIdentityProvider, scope rbacutils.TRbacScope) *sqlchemy.SQuery { - if owner != nil { - switch scope { - case rbacutils.ScopeProject: - ownerProjectid := owner.GetProjectId() - if len(ownerProjectid) > 0 { - subq := SharedResourceManager.Query("resource_id") - subq = subq.Equals("resource_type", manager.Keyword()) - subq = subq.Equals("target_project_id", ownerProjectid) - subq = subq.Equals("owner_project_id", q.Field("tenant_id")) - q = q.Filter(sqlchemy.OR( - sqlchemy.Equals(q.Field("tenant_id"), ownerProjectid), - sqlchemy.AND( - sqlchemy.IsTrue(q.Field("is_public")), - sqlchemy.Equals(q.Field("public_scope"), rbacutils.ScopeSystem), - ), - sqlchemy.AND( - sqlchemy.IsTrue(q.Field("is_public")), - sqlchemy.Equals(q.Field("public_scope"), rbacutils.ScopeDomain), - sqlchemy.Equals(q.Field("domain_id"), owner.GetProjectDomainId()), - ), - sqlchemy.AND( - sqlchemy.IsFalse(q.Field("is_public")), - sqlchemy.In(q.Field("id"), subq.SubQuery()), - ), - )) - } - case rbacutils.ScopeDomain: - if len(owner.GetProjectDomainId()) > 0 { - q = q.Filter(sqlchemy.OR( - sqlchemy.Equals(q.Field("domain_id"), owner.GetProjectDomainId()), - sqlchemy.AND( - sqlchemy.IsTrue(q.Field("is_public")), - sqlchemy.Equals(q.Field("public_scope"), rbacutils.ScopeSystem), - ), - )) - } - } - } - return q + return SharableManagerFilterByOwner(manager.GetISharableVirtualModelManager(), q, owner, scope) } func (model *SSharableVirtualResourceBase) AllowGetDetails(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject) bool { - return model.IsOwner(userCred) || model.IsPublic || IsAllowGet(rbacutils.ScopeSystem, userCred, model) + return model.IsOwner(userCred) || model.IsSharable(userCred) || IsAllowGet(rbacutils.ScopeSystem, userCred, model) } func (model *SSharableVirtualResourceBase) IsSharable(reqUsrId mcclient.IIdentityProvider) bool { - if model.IsPublic { - switch rbacutils.String2Scope(model.PublicScope) { - case rbacutils.ScopeSystem: - return true - case rbacutils.ScopeDomain: - ownerId := model.GetOwnerId() - if ownerId != nil && ownerId.GetProjectDomainId() == reqUsrId.GetProjectDomainId() { - return true - } - } - } - return false + return SharableModelIsSharable(model.GetISharableVirtualModel(), reqUsrId) } -func (model *SSharableVirtualResourceBase) AllowPerformPublic(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, input apis.PerformProjectPublicInput) bool { - return IsAllowPerform(rbacutils.ScopeSystem, userCred, model, "public") +func (model *SSharableVirtualResourceBase) IsShared() bool { + return SharableModelIsShared(model) } -func (model *SSharableVirtualResourceBase) PerformPublic(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, input apis.PerformProjectPublicInput) (jsonutils.JSONObject, error) { - targetScope := rbacutils.String2ScopeDefault(input.Scope, rbacutils.ScopeSystem) - if targetScope == rbacutils.ScopeProject { - if len(input.SharedProjects) > 0 { - delProjects := make([]*SSharedResource, 0) - addProjects := make([]string, 0) - ops := make(map[string]*SSharedResource, 0) - nps := make([]string, 0) - srs := make([]SSharedResource, 0) - q := SharedResourceManager.Query() - err := q.Filter(sqlchemy.AND( - sqlchemy.Equals(q.Field("owner_project_id"), model.ProjectId), - sqlchemy.Equals(q.Field("resource_id"), model.GetId()), - sqlchemy.Equals(q.Field("resource_type"), model.GetModelManager().Keyword()), - )).All(&srs) - if err != nil { - return nil, httperrors.NewInternalServerError("Fetch project error %s", err) - } - for i := 0; i < len(srs); i++ { - ops[srs[i].TargetProjectId] = &srs[i] - } - for i := 0; i < len(input.SharedProjects); i++ { - sharedProject := input.SharedProjects[i] - tenant, err := TenantCacheManager.FetchTenantByIdOrName(ctx, sharedProject) - if err != nil { - return nil, httperrors.NewBadRequestError("fetch tenant %s error %s", sharedProject, err) - } - if tenant.DomainId != model.DomainId { - return nil, httperrors.NewBadRequestError("can't shared project to other domain") - } - if tenant.GetId() == model.ProjectId { - return nil, httperrors.NewBadRequestError("Can't share project to yourself") - } - nps = append(nps, tenant.GetId()) - if _, ok := ops[tenant.GetId()]; !ok { - addProjects = append(addProjects, tenant.GetId()) - } - } - for k, v := range ops { - if !utils.IsInStringArray(k, nps) { - delProjects = append(delProjects, v) - } - } +func (model *SSharableVirtualResourceBase) AllowPerformPublic(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, input apis.PerformPublicInput) bool { + return true +} - for i := 0; i < len(addProjects); i++ { - sharedResource := new(SSharedResource) - sharedResource.ResourceType = model.GetModelManager().Keyword() - sharedResource.ResourceId = model.Id - sharedResource.OwnerProjectId = model.ProjectId - sharedResource.TargetProjectId = addProjects[i] - if insetErr := SharedResourceManager.TableSpec().Insert(sharedResource); insetErr != nil { - return nil, httperrors.NewInternalServerError("Insert shared resource failed %s", insetErr) - } - } - for i := 0; i < len(delProjects); i++ { - delProjects[i].SetModelManager(SharedResourceManager, delProjects[i]) - if err := delProjects[i].Delete(ctx, userCred); err != nil { - return nil, httperrors.NewInternalServerError("Unshare project failed %s", err) - } - } - - if (len(addProjects) + len(srs) - len(delProjects)) > 0 { - diff, err := Update(model, func() error { - model.IsPublic = false - model.PublicScope = string(targetScope) - return nil - }) - if err == nil { - OpsLog.LogEvent(model, ACT_UPDATE, diff, userCred) - } else { - return nil, err - } - } - } else { - return nil, httperrors.NewMissingParameterError("shared_projects") - } - } else { - allowScope := policy.PolicyManager.AllowScope(userCred, consts.GetServiceType(), model.GetModelManager().KeywordPlural(), policy.PolicyActionPerform, "public") - if targetScope.HigherThan(allowScope) { - return nil, httperrors.NewForbiddenError("not enough privilege") - } - if targetScope != rbacutils.ScopeSystem && targetScope != rbacutils.ScopeDomain { - return nil, httperrors.NewInputParameterError("invalid scope %s", targetScope) - } - diff, err := Update(model, func() error { - model.IsPublic = true - model.PublicScope = string(targetScope) - return nil - }) - if err == nil { - OpsLog.LogEvent(model, ACT_UPDATE, diff, userCred) - } else { - return nil, err - } +func (model *SSharableVirtualResourceBase) PerformPublic(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, input apis.PerformPublicInput) (jsonutils.JSONObject, error) { + err := SharablePerformPublic(model, ctx, userCred, input) + if err != nil { + return nil, errors.Wrap(err, "SharablePerformPublic") } - model.GetIStandaloneModel().ClearSchedDescCache() return nil, nil } -func (model *SSharableVirtualResourceBase) AllowPerformPrivate(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, input apis.PerformProjectPrivateInput) bool { - return IsAllowPerform(rbacutils.ScopeSystem, userCred, model, "private") +func (model *SSharableVirtualResourceBase) AllowPerformPrivate(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, input apis.PerformPrivateInput) bool { + return true } -func (model *SSharableVirtualResourceBase) PerformPrivate(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, input apis.PerformProjectPrivateInput) (jsonutils.JSONObject, error) { - if model.IsPublic { - allowScope := policy.PolicyManager.AllowScope(userCred, consts.GetServiceType(), model.GetModelManager().KeywordPlural(), policy.PolicyActionPerform, "private") - requireScope := rbacutils.String2ScopeDefault(model.PublicScope, rbacutils.ScopeSystem) - if requireScope.HigherThan(allowScope) { - return nil, httperrors.NewForbiddenError("not enough privileges: allow %s require %s", allowScope, requireScope) - } - diff, err := Update(model, func() error { - model.PublicScope = string(rbacutils.ScopeNone) - model.IsPublic = false - return nil - }) - if err == nil { - OpsLog.LogEvent(model, ACT_UPDATE, diff, userCred) - } else { - return nil, httperrors.NewInternalServerError("Update shared resource error: %s", err) - } +func (model *SSharableVirtualResourceBase) PerformPrivate(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, input apis.PerformPrivateInput) (jsonutils.JSONObject, error) { + err := SharablePerformPrivate(model, ctx, userCred) + if err != nil { + return nil, errors.Wrap(err, "SharablePerformPrivate") } - - if err := SharedResourceManager.CleanModelSharedProjects(ctx, userCred, &model.SVirtualResourceBase); err != nil { - return nil, err - } - model.GetIStandaloneModel().ClearSchedDescCache() return nil, nil } @@ -249,34 +92,6 @@ func (model *SSharableVirtualResourceBase) GetISharableVirtualModel() ISharableV return model.GetVirtualObject().(ISharableVirtualModel) } -func (model *SSharableVirtualResourceBase) GetSharedProjects() []string { - sharedResources := make([]SSharedResource, 0) - res := make([]string, 0) - SharedResourceManager.Query().Equals("resource_type", model.GetModelManager().Keyword()).Equals("resource_id", model.GetId()).All(&sharedResources) - for i := 0; i < len(sharedResources); i++ { - res = append(res, sharedResources[i].TargetProjectId) - } - return res -} - -/*func (model *SSharableVirtualResourceBase) getMoreDetails(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, out apis.SharableVirtualResourceDetails) apis.SharableVirtualResourceDetails { - out.SharedProjects = []apis.SharedProject{} - projects := model.GetSharedProjects() - for i := 0; i < len(projects); i++ { - tenant, err := TenantCacheManager.FetchTenantByIdOrName(ctx, projects[i]) - if err != nil { - log.Errorf("failed fetch tenant by id %s", projects[i]) - continue - } - project := apis.SharedProject{ - Id: tenant.GetId(), - Name: tenant.GetName(), - } - out.SharedProjects = append(out.SharedProjects, project) - } - return out -}*/ - func (manager *SSharableVirtualResourceBaseManager) ValidateCreateData(ctx context.Context, userCred mcclient.TokenCredential, ownerId mcclient.IIdentityProvider, query jsonutils.JSONObject, input apis.SharableVirtualResourceCreateInput) (apis.SharableVirtualResourceCreateInput, error) { var err error input.VirtualResourceCreateInput, err = manager.SVirtualResourceBaseManager.ValidateCreateData(ctx, userCred, ownerId, query, input.VirtualResourceCreateInput) @@ -296,15 +111,9 @@ func (manager *SSharableVirtualResourceBaseManager) ListItemFilter( if err != nil { return nil, errors.Wrap(err, "SVirtualResourceBaseManager.ListItemFilter") } - if query.IsPublic != nil { - if *query.IsPublic { - q = q.IsTrue("is_public") - } else { - q = q.IsFalse("is_public") - } - } - if len(query.PublicScope) > 0 { - q = q.Equals("public_scope", query.PublicScope) + q, err = manager.SSharableBaseResourceManager.ListItemFilter(ctx, q, userCred, query.SharableResourceBaseListInput) + if err != nil { + return nil, errors.Wrap(err, "SSharableBaseResourceManager.ListItemFilter") } return q, nil } @@ -337,33 +146,51 @@ func (manager *SSharableVirtualResourceBaseManager) FetchCustomizeColumns( fields stringutils2.SSortedStrings, isList bool, ) []apis.SharableVirtualResourceDetails { - rows := make([]apis.SharableVirtualResourceDetails, len(objs)) + virtRows := manager.SVirtualResourceBaseManager.FetchCustomizeColumns(ctx, userCred, query, objs, fields, isList) - resIds := make([]string, len(objs)) + shareRows := manager.SSharableBaseResourceManager.FetchCustomizeColumns(ctx, userCred, query, objs, fields, isList) + for i := range rows { - resIds[i] = objs[i].(ISharableVirtualModel).GetId() rows[i] = apis.SharableVirtualResourceDetails{ - VirtualResourceDetails: virtRows[i], - } - } - - tenants := TenantCacheManager.GetTenantQuery().SubQuery() - resources := SharedResourceManager.Query().Equals("resource_type", manager.Keyword()).SubQuery() - q := tenants.Query(tenants.Field("id"), tenants.Field("name"), resources.Field("resource_id")) - q = q.Join(resources, sqlchemy.Equals(q.Field("id"), resources.Field("target_project_id"))) - projList := make(map[string][]apis.SharedProject) - err := FetchQueryObjectsByIds(q, "resource_id", resIds, &projList) - if err != nil { - log.Errorf("FetchQueryObjectsByIds fail %s", err) - return rows - } - - for i := range rows { - if projs, ok := projList[resIds[i]]; ok { - rows[i].SharedProjects = projs + VirtualResourceDetails: virtRows[i], + SharableResourceBaseInfo: shareRows[i], } } return rows } + +func (model *SSharableVirtualResourceBase) ValidateUpdateData( + ctx context.Context, + userCred mcclient.TokenCredential, + query jsonutils.JSONObject, + input apis.SharableVirtualResourceBaseUpdateInput, +) (apis.SharableVirtualResourceBaseUpdateInput, error) { + var err error + input.VirtualResourceBaseUpdateInput, err = model.SVirtualResourceBase.ValidateUpdateData(ctx, userCred, query, input.VirtualResourceBaseUpdateInput) + if err != nil { + return input, errors.Wrap(err, "SVirtualResourceBase.ValidateUpdateData") + } + return input, nil +} + +func (model *SSharableVirtualResourceBase) GetSharedProjects() []string { + return SharableGetSharedProjects(model, SharedTargetProject) +} + +func (model *SSharableVirtualResourceBase) GetSharedDomains() []string { + return SharableGetSharedProjects(model, SharedTargetDomain) +} + +func (model *SSharableVirtualResourceBase) PerformChangeOwner( + ctx context.Context, + userCred mcclient.TokenCredential, + query jsonutils.JSONObject, + input apis.PerformChangeProjectOwnerInput, +) (jsonutils.JSONObject, error) { + if model.IsShared() { + return nil, errors.Wrap(httperrors.ErrInvalidStatus, "cannot change owner when shared!") + } + return model.SVirtualResourceBase.PerformChangeOwner(ctx, userCred, query, input) +} diff --git a/pkg/cloudcommon/db/sharedresource.go b/pkg/cloudcommon/db/sharedresource.go index 279235c93f..e52f1f78a2 100644 --- a/pkg/cloudcommon/db/sharedresource.go +++ b/pkg/cloudcommon/db/sharedresource.go @@ -16,14 +16,24 @@ package db import ( "context" + "database/sql" - "yunion.io/x/sqlchemy" + "yunion.io/x/pkg/errors" + "yunion.io/x/onecloud/pkg/cloudcommon/consts" + "yunion.io/x/onecloud/pkg/cloudcommon/policy" "yunion.io/x/onecloud/pkg/httperrors" "yunion.io/x/onecloud/pkg/mcclient" + "yunion.io/x/onecloud/pkg/util/rbacutils" + "yunion.io/x/onecloud/pkg/util/stringutils2" ) -// sharing resoure between project +const ( + SharedTargetProject = "project" + SharedTargetDomain = "domain" +) + +// sharing resource between projects/domains type SSharedResource struct { SResourceBase @@ -33,6 +43,8 @@ type SSharedResource struct { ResourceId string `width:"128" charset:"ascii" nullable:"false" index:"true" list:"user" json:"resource_id"` OwnerProjectId string `width:"128" charset:"ascii" nullable:"false" index:"true" list:"user" json:"owner_project_id"` TargetProjectId string `width:"128" charset:"ascii" nullable:"false" index:"true" list:"user" json:"target_project_id"` + + TargetType string `width:"8" charset:"ascii" default:"project" nullable:"false" list:"user" json:"target_type"` } type SSharedResourceManager struct { @@ -52,22 +64,132 @@ func init() { } } -func (manager *SSharedResourceManager) CleanModelSharedProjects(ctx context.Context, userCred mcclient.TokenCredential, model *SVirtualResourceBase) error { - srs := make([]SSharedResource, 0) - q := manager.Query() - err := q.Filter(sqlchemy.AND( - sqlchemy.Equals(q.Field("owner_project_id"), model.ProjectId), - sqlchemy.Equals(q.Field("resource_id"), model.GetId()), - sqlchemy.Equals(q.Field("resource_type"), model.GetModelManager().Keyword()), - )).All(&srs) - if err != nil { - return httperrors.NewInternalServerError("Fetch project error %s", err) - } - for i := 0; i < len(srs); i++ { - srs[i].SetModelManager(manager, &srs[i]) - if err := srs[i].Delete(ctx, userCred); err != nil { - return httperrors.NewInternalServerError("Unshare project failed %s", err) +func (manager *SSharedResourceManager) CleanModelShares(ctx context.Context, userCred mcclient.TokenCredential, model ISharableBaseModel) error { + var err error + resScope := model.GetModelManager().ResourceScope() + switch resScope { + case rbacutils.ScopeProject: + _, err = manager.shareToTarget(ctx, userCred, model, SharedTargetProject, nil) + if err != nil { + return errors.Wrap(err, "remove shared project") + } + _, err = manager.shareToTarget(ctx, userCred, model, SharedTargetDomain, nil) + if err != nil { + return errors.Wrap(err, "remove shared domain") + } + case rbacutils.ScopeDomain: + _, err = manager.shareToTarget(ctx, userCred, model, SharedTargetDomain, nil) + if err != nil { + return errors.Wrap(err, "remove shared domain") } } return nil } + +func (manager *SSharedResourceManager) shareToTarget( + ctx context.Context, + userCred mcclient.TokenCredential, + model ISharableBaseModel, + targetType string, + targetIds []string, +) ([]string, error) { + var requireScope rbacutils.TRbacScope + resScope := model.GetModelManager().ResourceScope() + switch resScope { + case rbacutils.ScopeProject: + switch targetType { + case SharedTargetProject: + // should have domain-level privileges + // cannot share to a project across domain + requireScope = rbacutils.ScopeDomain + case SharedTargetDomain: + // should have system-level privileges + requireScope = rbacutils.ScopeSystem + } + case rbacutils.ScopeDomain: + switch targetType { + case SharedTargetDomain: + // should have system-level privileges + requireScope = rbacutils.ScopeSystem + case SharedTargetProject: + return nil, errors.Wrap(httperrors.ErrNotSupported, "cannot share a domain resource to specific project") + } + default: + return nil, errors.Wrap(httperrors.ErrNotSupported, "cannot share a non-project/domain resource") + } + + srs := make([]SSharedResource, 0) + q := SharedResourceManager.Query() + q = q.Equals("resource_type", model.Keyword()) + q = q.Equals("resource_id", model.GetId()) + q = q.Equals("target_type", targetType) + err := FetchModelObjects(SharedResourceManager, q, &srs) + if err != nil && errors.Cause(err) != sql.ErrNoRows { + return nil, errors.Wrap(err, "Fetch shared project") + } + srsMap := make(map[string]*SSharedResource) + _existIds := make([]string, len(srs)) + for i := 0; i < len(srs); i++ { + _existIds[i] = srs[i].TargetProjectId + srsMap[srs[i].TargetProjectId] = &srs[i] + } + existIds := stringutils2.NewSortedStrings(_existIds) + + newIds := stringutils2.NewSortedStrings([]string{}) + modelOwnerId := model.GetOwnerId() + for i := 0; i < len(targetIds); i++ { + switch targetType { + case SharedTargetProject: + tenant, err := TenantCacheManager.FetchTenantByIdOrName(ctx, targetIds[i]) + if err != nil { + return nil, errors.Wrapf(err, "fetch tenant %s error", targetIds[i]) + } + if tenant.DomainId != modelOwnerId.GetProjectDomainId() { + return nil, httperrors.NewBadRequestError("can't shared project to other domain") + } + if tenant.GetId() == modelOwnerId.GetProjectId() { + return nil, httperrors.NewBadRequestError("can't share to self project") + } + newIds = stringutils2.Append(newIds, tenant.GetId()) + case SharedTargetDomain: + domain, err := TenantCacheManager.FetchDomainByIdOrName(ctx, targetIds[i]) + if err != nil { + return nil, errors.Wrapf(err, "fetch domain %s error", targetIds[i]) + } + if domain.GetId() == modelOwnerId.GetProjectDomainId() { + return nil, httperrors.NewBadRequestError("can't share to self domain") + } + newIds = stringutils2.Append(newIds, domain.GetId()) + } + } + delIds, keepIds, addIds := stringutils2.Split(existIds, newIds) + + if len(delIds) == 0 && len(addIds) == 0 { + return keepIds, nil + } + + allowScope := policy.PolicyManager.AllowScope(userCred, consts.GetServiceType(), model.KeywordPlural(), policy.PolicyActionPerform, "public") + if requireScope.HigherThan(allowScope) { + return nil, errors.Wrapf(httperrors.ErrNotSufficientPrivilege, "require %s allow %s", requireScope, allowScope) + } + + for _, targetId := range delIds { + sr := srsMap[targetId] + if err := sr.Delete(ctx, userCred); err != nil { + return nil, errors.Wrap(err, "delete") + } + } + for _, targetId := range addIds { + sharedResource := new(SSharedResource) + sharedResource.ResourceType = model.Keyword() + sharedResource.ResourceId = model.GetId() + sharedResource.TargetProjectId = targetId + sharedResource.TargetType = targetType + if insetErr := SharedResourceManager.TableSpec().Insert(sharedResource); insetErr != nil { + return nil, httperrors.NewInternalServerError("Insert shared resource failed %s", insetErr) + } + } + + keepIds = append(keepIds, addIds...) + return keepIds, nil +} diff --git a/pkg/cloudcommon/db/standalone.go b/pkg/cloudcommon/db/standalone.go index 3047258eae..3070e2b169 100644 --- a/pkg/cloudcommon/db/standalone.go +++ b/pkg/cloudcommon/db/standalone.go @@ -572,3 +572,23 @@ func (manager *SStandaloneResourceBaseManager) ListItemExportKeys(ctx context.Co return q, nil } + +func (model *SStandaloneResourceBase) ValidateUpdateData(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, input apis.StandaloneResourceBaseUpdateInput) (apis.StandaloneResourceBaseUpdateInput, error) { + var err error + input.ResourceBaseUpdateInput, err = model.SResourceBase.ValidateUpdateData(ctx, userCred, query, input.ResourceBaseUpdateInput) + if err != nil { + return input, errors.Wrap(err, "SModelBase.ValidateUpdateData") + } + + if len(input.Name) > 0 { + err = alterNameValidator(model.GetIStandaloneModel(), input.Name) + if err != nil { + return input, errors.Wrap(err, "alterNameValidator") + } + } + return input, nil +} + +func (model *SStandaloneResourceBase) IsShared() bool { + return false +} diff --git a/pkg/cloudcommon/db/statusbase.go b/pkg/cloudcommon/db/statusbase.go index 9c9c16b29d..b518bfe01b 100644 --- a/pkg/cloudcommon/db/statusbase.go +++ b/pkg/cloudcommon/db/statusbase.go @@ -39,22 +39,22 @@ type SStatusResourceBase struct { type IStatusBase interface { IModel SetStatusValue(status string) - GetStatusValue() string + GetStatus() string } func (model *SStatusResourceBase) SetStatusValue(status string) { model.Status = status } -func (model SStatusResourceBase) GetStatusValue() string { +func (model SStatusResourceBase) GetStatus() string { return model.Status } func statusBaseSetStatus(model IStatusBase, userCred mcclient.TokenCredential, status string, reason string) error { - if model.GetStatusValue() == status { + if model.GetStatus() == status { return nil } - oldStatus := model.GetStatusValue() + oldStatus := model.GetStatus() _, err := Update(model, func() error { model.SetStatusValue(status) return nil diff --git a/pkg/cloudcommon/db/statusdomainresource.go b/pkg/cloudcommon/db/statusdomainresource.go index 782b9ea733..6581439038 100644 --- a/pkg/cloudcommon/db/statusdomainresource.go +++ b/pkg/cloudcommon/db/statusdomainresource.go @@ -146,3 +146,17 @@ func (model *SStatusDomainLevelResourceBase) GetExtraDetails( ) (apis.StatusDomainLevelResourceDetails, error) { return apis.StatusDomainLevelResourceDetails{}, nil } + +func (model *SStatusDomainLevelResourceBase) ValidateUpdateData( + ctx context.Context, + userCred mcclient.TokenCredential, + query jsonutils.JSONObject, + input apis.StatusDomainLevelResourceBaseUpdateInput, +) (apis.StatusDomainLevelResourceBaseUpdateInput, error) { + var err error + input.DomainLevelResourceBaseUpdateInput, err = model.SDomainLevelResourceBase.ValidateUpdateData(ctx, userCred, query, input.DomainLevelResourceBaseUpdateInput) + if err != nil { + return input, errors.Wrap(err, "SDomainLevelResourceBase.ValidateUpdateData") + } + return input, nil +} diff --git a/pkg/cloudcommon/db/statusinfrasresource.go b/pkg/cloudcommon/db/statusinfrasresource.go new file mode 100644 index 0000000000..f16c59667d --- /dev/null +++ b/pkg/cloudcommon/db/statusinfrasresource.go @@ -0,0 +1,162 @@ +// 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 db + +import ( + "context" + + "yunion.io/x/jsonutils" + "yunion.io/x/pkg/errors" + "yunion.io/x/sqlchemy" + + "yunion.io/x/onecloud/pkg/apis" + "yunion.io/x/onecloud/pkg/httperrors" + "yunion.io/x/onecloud/pkg/mcclient" + "yunion.io/x/onecloud/pkg/util/stringutils2" +) + +type SStatusInfrasResourceBase struct { + SInfrasResourceBase + SStatusResourceBase +} + +type SStatusInfrasResourceBaseManager struct { + SInfrasResourceBaseManager + SStatusResourceBaseManager +} + +func NewStatusInfrasResourceBaseManager(dt interface{}, tableName string, keyword string, keywordPlural string) SStatusInfrasResourceBaseManager { + return SStatusInfrasResourceBaseManager{ + SInfrasResourceBaseManager: NewInfrasResourceBaseManager(dt, tableName, keyword, keywordPlural), + } +} + +func (model *SStatusInfrasResourceBase) AllowGetDetailsStatus(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject) bool { + return model.IsOwner(userCred) || IsDomainAllowGetSpec(userCred, model, "status") +} + +func (self *SStatusInfrasResourceBase) AllowPerformStatus(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, input apis.PerformStatusInput) bool { + return IsDomainAllowPerform(userCred, self, "status") +} + +func (self *SStatusInfrasResourceBase) PerformStatus(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, input apis.PerformStatusInput) (jsonutils.JSONObject, error) { + err := StatusBasePerformStatus(self, userCred, input) + if err != nil { + return nil, errors.Wrap(err, "StatusBasePerformStatus") + } + return nil, nil +} + +func (model *SStatusInfrasResourceBase) SetStatus(userCred mcclient.TokenCredential, status string, reason string) error { + return statusBaseSetStatus(model, userCred, status, reason) +} + +func (manager *SStatusInfrasResourceBaseManager) ValidateCreateData( + ctx context.Context, + userCred mcclient.TokenCredential, + ownerId mcclient.IIdentityProvider, + query jsonutils.JSONObject, + input apis.StatusInfrasResourceBaseCreateInput, +) (apis.StatusInfrasResourceBaseCreateInput, error) { + var err error + input.InfrasResourceBaseCreateInput, err = manager.SInfrasResourceBaseManager.ValidateCreateData(ctx, userCred, ownerId, query, input.InfrasResourceBaseCreateInput) + if err != nil { + return input, errors.Wrap(err, "SInfrasResourceBaseManager.ValidateCreateData") + } + return input, nil +} + +func (manager *SStatusInfrasResourceBaseManager) ListItemFilter( + ctx context.Context, + q *sqlchemy.SQuery, + userCred mcclient.TokenCredential, + query apis.StatusInfrasResourceBaseListInput, +) (*sqlchemy.SQuery, error) { + q, err := manager.SInfrasResourceBaseManager.ListItemFilter(ctx, q, userCred, query.InfrasResourceBaseListInput) + if err != nil { + return nil, errors.Wrap(err, "SInfrasResourceBaseManager.ListItemFilter") + } + q, err = manager.SStatusResourceBaseManager.ListItemFilter(ctx, q, userCred, query.StatusResourceBaseListInput) + if err != nil { + return nil, errors.Wrap(err, "SStatusResourceBaseManager.ListItemFilter") + } + return q, nil +} + +func (manager *SStatusInfrasResourceBaseManager) OrderByExtraFields( + ctx context.Context, + q *sqlchemy.SQuery, + userCred mcclient.TokenCredential, + query apis.StatusInfrasResourceBaseListInput, +) (*sqlchemy.SQuery, error) { + q, err := manager.SInfrasResourceBaseManager.OrderByExtraFields(ctx, q, userCred, query.InfrasResourceBaseListInput) + if err != nil { + return nil, errors.Wrap(err, "SInfrasResourceBaseManager.OrderByExtraFields") + } + q, err = manager.SStatusResourceBaseManager.OrderByExtraFields(ctx, q, userCred, query.StatusResourceBaseListInput) + if err != nil { + return nil, errors.Wrap(err, "SStatusResourceBaseManager.OrderByExtraFields") + } + return q, nil +} + +func (manager *SStatusInfrasResourceBaseManager) QueryDistinctExtraField(q *sqlchemy.SQuery, field string) (*sqlchemy.SQuery, error) { + q, err := manager.SInfrasResourceBaseManager.QueryDistinctExtraField(q, field) + if err == nil { + return q, nil + } + return q, httperrors.ErrNotFound +} + +func (manager *SStatusInfrasResourceBaseManager) FetchCustomizeColumns( + ctx context.Context, + userCred mcclient.TokenCredential, + query jsonutils.JSONObject, + objs []interface{}, + fields stringutils2.SSortedStrings, + isList bool, +) []apis.StatusInfrasResourceBaseDetails { + rows := make([]apis.StatusInfrasResourceBaseDetails, len(objs)) + infRows := manager.SInfrasResourceBaseManager.FetchCustomizeColumns(ctx, userCred, query, objs, fields, isList) + for i := range rows { + rows[i] = apis.StatusInfrasResourceBaseDetails{ + InfrasResourceBaseDetails: infRows[i], + } + } + return rows +} + +func (model *SStatusInfrasResourceBase) GetExtraDetails( + ctx context.Context, + userCred mcclient.TokenCredential, + query jsonutils.JSONObject, + isList bool, +) (apis.StatusInfrasResourceBaseDetails, error) { + return apis.StatusInfrasResourceBaseDetails{}, nil +} + +func (model *SStatusInfrasResourceBase) ValidateUpdateData( + ctx context.Context, + userCred mcclient.TokenCredential, + query jsonutils.JSONObject, + input apis.StatusInfrasResourceBaseUpdateInput, +) (apis.StatusInfrasResourceBaseUpdateInput, error) { + var err error + input.InfrasResourceBaseUpdateInput, err = model.SInfrasResourceBase.ValidateUpdateData(ctx, userCred, query, input.InfrasResourceBaseUpdateInput) + if err != nil { + return input, errors.Wrap(err, "SInfrasResourceBase.ValidateUpdateData") + } + return input, nil +} diff --git a/pkg/cloudcommon/db/statusstandalone.go b/pkg/cloudcommon/db/statusstandalone.go index bfdb2f1669..00be874f43 100644 --- a/pkg/cloudcommon/db/statusstandalone.go +++ b/pkg/cloudcommon/db/statusstandalone.go @@ -63,10 +63,6 @@ func (model *SStatusStandaloneResourceBase) SetStatus(userCred mcclient.TokenCre return statusBaseSetStatus(model, userCred, status, reason) } -func (model *SStatusStandaloneResourceBase) GetStatus() string { - return model.Status -} - func (manager *SStatusStandaloneResourceBaseManager) ValidateCreateData(ctx context.Context, userCred mcclient.TokenCredential, ownerId mcclient.IIdentityProvider, query jsonutils.JSONObject, input apis.StatusStandaloneResourceCreateInput) (apis.StatusStandaloneResourceCreateInput, error) { var err error input.StandaloneResourceCreateInput, err = manager.SStandaloneResourceBaseManager.ValidateCreateData(ctx, userCred, ownerId, query, input.StandaloneResourceCreateInput) @@ -139,3 +135,17 @@ func (model *SStatusStandaloneResourceBase) GetExtraDetails( ) (apis.StatusStandaloneResourceDetails, error) { return apis.StatusStandaloneResourceDetails{}, nil } + +func (model *SStatusStandaloneResourceBase) ValidateUpdateData( + ctx context.Context, + userCred mcclient.TokenCredential, + query jsonutils.JSONObject, + input apis.StatusStandaloneResourceBaseUpdateInput, +) (apis.StatusStandaloneResourceBaseUpdateInput, error) { + var err error + input.StandaloneResourceBaseUpdateInput, err = model.SStandaloneResourceBase.ValidateUpdateData(ctx, userCred, query, input.StandaloneResourceBaseUpdateInput) + if err != nil { + return input, errors.Wrap(err, "SStandaloneResourceBase.ValidateUpdateData") + } + return input, nil +} diff --git a/pkg/cloudcommon/db/tenantcache.go b/pkg/cloudcommon/db/tenantcache.go index 073fb45245..d93d5fc637 100644 --- a/pkg/cloudcommon/db/tenantcache.go +++ b/pkg/cloudcommon/db/tenantcache.go @@ -21,6 +21,8 @@ import ( "runtime/debug" "time" + "yunion.io/x/onecloud/pkg/httperrors" + "yunion.io/x/jsonutils" "yunion.io/x/log" "yunion.io/x/pkg/errors" @@ -115,6 +117,13 @@ func (manager *STenantCacheManager) fetchTenant(ctx context.Context, idStr strin q = manager.GetTenantQuery() } q = filter(q) + tcnt, err := q.CountWithError() + if err != nil { + return nil, errors.Wrap(err, "CountWithError") + } + if tcnt > 1 { + return nil, httperrors.ErrDuplicateName + } tobj, err := NewModelObject(manager) if err != nil { return nil, errors.Wrap(err, "NewModelObject") diff --git a/pkg/cloudcommon/db/virtualjointbase.go b/pkg/cloudcommon/db/virtualjointbase.go index 8e5ea5ca30..b1b88a96f0 100644 --- a/pkg/cloudcommon/db/virtualjointbase.go +++ b/pkg/cloudcommon/db/virtualjointbase.go @@ -180,3 +180,17 @@ func (manager *SVirtualJointResourceBaseManager) OrderByExtraFields( return q, nil } + +func (self *SVirtualJointResourceBase) ValidateUpdateData( + ctx context.Context, + userCred mcclient.TokenCredential, + query jsonutils.JSONObject, + input apis.VirtualJointResourceBaseUpdateInput, +) (apis.VirtualJointResourceBaseUpdateInput, error) { + var err error + input.JointResourceBaseUpdateInput, err = self.SJointResourceBase.ValidateUpdateData(ctx, userCred, query, input.JointResourceBaseUpdateInput) + if err != nil { + return input, errors.Wrap(err, "SJointResourceBase.ValidateUpdateData") + } + return input, nil +} diff --git a/pkg/cloudcommon/db/virtualresource.go b/pkg/cloudcommon/db/virtualresource.go index 2a94c470cb..1e27219b51 100644 --- a/pkg/cloudcommon/db/virtualresource.go +++ b/pkg/cloudcommon/db/virtualresource.go @@ -60,6 +60,7 @@ type SVirtualResourceBase struct { // 是否是系统资源 IsSystem bool `nullable:"true" default:"false" list:"admin" create:"optional" json:"is_system"` + // 资源放入回收站时间 PendingDeletedAt time.Time `json:"pending_deleted_at"` // 资源是否处于回收站中 PendingDeleted bool `nullable:"false" default:"false" index:"true" get:"user" list:"user" json:"pending_deleted"` @@ -276,6 +277,25 @@ func (model *SVirtualResourceBase) PerformChangeOwner(ctx context.Context, userC }) return nil, nil } + + var requireScope rbacutils.TRbacScope + if ownerId.GetProjectDomainId() != model.DomainId { + // change domain, do check + if managed, ok := model.GetIVirtualModel().(IManagedResoucceBase); ok { + if !managed.CanShareToDomain(ownerId.GetProjectDomainId()) { + return nil, errors.Wrap(httperrors.ErrForbidden, "cann't share across domain") + } + } + requireScope = rbacutils.ScopeSystem + } else { + requireScope = rbacutils.ScopeDomain + } + + allowScope := policy.PolicyManager.AllowScope(userCred, consts.GetServiceType(), model.KeywordPlural(), policy.PolicyActionPerform, "change-owner") + if requireScope.HigherThan(allowScope) { + return nil, errors.Wrapf(httperrors.ErrNotSufficientPrivilege, "require %s allow %s", requireScope, allowScope) + } + q := manager.Query().Equals("name", model.GetName()) q = manager.FilterByOwner(q, ownerId, manager.NamespaceScope()) q = manager.FilterBySystemAttributes(q, nil, nil, manager.ResourceScope()) @@ -295,8 +315,10 @@ func (model *SVirtualResourceBase) PerformChangeOwner(ctx context.Context, userC } // clean shared projects before update project id - if err := SharedResourceManager.CleanModelSharedProjects(ctx, userCred, model); err != nil { - return nil, err + if sharedModel, ok := model.GetIVirtualModel().(ISharableBaseModel); ok { + if err := SharedResourceManager.CleanModelShares(ctx, userCred, sharedModel); err != nil { + return nil, err + } } _, err = Update(model, func() error { @@ -495,3 +517,17 @@ func (manager *SVirtualResourceBaseManager) QueryDistinctExtraField(q *sqlchemy. } return q, httperrors.ErrNotFound } + +func (model *SVirtualResourceBase) ValidateUpdateData( + ctx context.Context, + userCred mcclient.TokenCredential, + query jsonutils.JSONObject, + input apis.VirtualResourceBaseUpdateInput, +) (apis.VirtualResourceBaseUpdateInput, error) { + var err error + input.StatusStandaloneResourceBaseUpdateInput, err = model.SStatusStandaloneResourceBase.ValidateUpdateData(ctx, userCred, query, input.StatusStandaloneResourceBaseUpdateInput) + if err != nil { + return input, errors.Wrap(err, "SStatusStandaloneResourceBase.ValidateUpdateData") + } + return input, nil +} diff --git a/pkg/cloudcommon/elect/doc.go b/pkg/cloudcommon/elect/doc.go index 03dc698e6e..e880fcb3ea 100644 --- a/pkg/cloudcommon/elect/doc.go +++ b/pkg/cloudcommon/elect/doc.go @@ -1 +1,15 @@ +// 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 elect // import "yunion.io/x/onecloud/pkg/cloudcommon/elect" diff --git a/pkg/cloudcommon/options/changes.go b/pkg/cloudcommon/options/changes.go index 6f1bdf94e4..428641c933 100644 --- a/pkg/cloudcommon/options/changes.go +++ b/pkg/cloudcommon/options/changes.go @@ -34,6 +34,9 @@ func OnBaseOptionsChange(oOpts, nOpts interface{}) bool { if oldOpts.NonDefaultDomainProjects != newOpts.NonDefaultDomainProjects { consts.SetNonDefaultDomainProjects(newOpts.NonDefaultDomainProjects) } + if oldOpts.DomainizedNamespace != newOpts.DomainizedNamespace { + consts.SetDomainizedNamespace(newOpts.DomainizedNamespace) + } return false } diff --git a/pkg/cloudcommon/options/options.go b/pkg/cloudcommon/options/options.go index 1fe335b5fb..7919876049 100644 --- a/pkg/cloudcommon/options/options.go +++ b/pkg/cloudcommon/options/options.go @@ -86,6 +86,8 @@ type BaseOptions struct { TimeZone string `help:"time zone" default:"Asia/Shanghai"` + DomainizedNamespace bool `help:"turn on global name space, default is on" default:"false" json:"global_namespace,allowfalse"` + structarg.BaseOptions } @@ -288,4 +290,6 @@ func ParseOptions(optStruct interface{}, args []string, configFileName string, s if len(optionsRef.Region) > 0 { consts.SetRegion(optionsRef.Region) } + + consts.SetDomainizedNamespace(optionsRef.DomainizedNamespace) } diff --git a/pkg/cloudcommon/policy/resources.go b/pkg/cloudcommon/policy/resources.go index 9f63225f04..ed8924f13f 100644 --- a/pkg/cloudcommon/policy/resources.go +++ b/pkg/cloudcommon/policy/resources.go @@ -18,35 +18,35 @@ import "yunion.io/x/pkg/utils" var ( computeSystemResources = []string{ - "hosts", "zones", - "storages", - "wires", - "globalvpcs", - "route_tables", "cloudregions", "serverskus", "cachedimages", "dynamicschedtags", "baremetalagents", "schedpolicies", - "isolated-devices", - "reservedips", "dnsrecords", "metadatas", "loadbalancerclusters", "loadbalanceragents", - "networkinterfaces", - "natgateways", - "natsentries", - "natdentries", + "isolated-devices", + "reservedips", } computeDomainResources = []string{ "cloudaccounts", "cloudproviders", "recyclebins", // migrate system resources to domain resources + "hosts", "vpcs", + "storages", + "wires", + "globalvpcs", + "route_tables", + "networkinterfaces", + "natgateways", + "natsentries", + "natdentries", } computeUserResources = []string{ "keypairs", diff --git a/pkg/cloudnet/models/routers.go b/pkg/cloudnet/models/routers.go index 96948d04df..405641f496 100644 --- a/pkg/cloudnet/models/routers.go +++ b/pkg/cloudnet/models/routers.go @@ -25,6 +25,7 @@ import ( "yunion.io/x/pkg/util/netutils" "yunion.io/x/onecloud/pkg/apis" + api "yunion.io/x/onecloud/pkg/apis/cloudnet" "yunion.io/x/onecloud/pkg/cloudcommon/db" "yunion.io/x/onecloud/pkg/cloudcommon/validators" "yunion.io/x/onecloud/pkg/httperrors" @@ -113,10 +114,14 @@ func (man *SRouterManager) getById(id string) (*SRouter, error) { return router, err } -func (router *SRouter) ValidateUpdateData(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, data *jsonutils.JSONDict) (*jsonutils.JSONDict, error) { - if _, err := router.SStandaloneResourceBase.ValidateUpdateData(ctx, userCred, query, data); err != nil { - return nil, err +func (router *SRouter) ValidateUpdateData(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, input api.RouterUpdateInput) (api.RouterUpdateInput, error) { + var err error + input.StandaloneResourceBaseUpdateInput, err = router.SStandaloneResourceBase.ValidateUpdateData(ctx, userCred, query, input.StandaloneResourceBaseUpdateInput) + if err != nil { + return input, errors.Wrap(err, "SStandaloneResourceBase.ValidateUpdateData") } + + data := jsonutils.Marshal(input).(*jsonutils.JSONDict) vs := []validators.IValidator{ validators.NewStringNonEmptyValidator("user"), validators.NewStringNonEmptyValidator("host"), @@ -129,11 +134,11 @@ func (router *SRouter) ValidateUpdateData(ctx context.Context, userCred mcclient for _, v := range vs { v.Optional(true) if err := v.Validate(data); err != nil { - return nil, err + return input, err } } - data.Set("_old_endpoint", jsonutils.NewString(router.endpointIP())) - return data, nil + input.OldEndpoint = router.endpointIP() + return input, nil } func (router *SRouter) PostUpdate(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, data jsonutils.JSONObject) { diff --git a/pkg/cloudnet/models/routes.go b/pkg/cloudnet/models/routes.go index 23f58512ba..5a036a87ae 100644 --- a/pkg/cloudnet/models/routes.go +++ b/pkg/cloudnet/models/routes.go @@ -23,6 +23,7 @@ import ( "yunion.io/x/sqlchemy" "yunion.io/x/onecloud/pkg/apis" + api "yunion.io/x/onecloud/pkg/apis/cloudnet" "yunion.io/x/onecloud/pkg/cloudcommon/db" "yunion.io/x/onecloud/pkg/cloudcommon/validators" "yunion.io/x/onecloud/pkg/httperrors" @@ -136,10 +137,13 @@ func (man *SRouteManager) ListItemFilter(ctx context.Context, q *sqlchemy.SQuery return q, nil } -func (route *SRoute) ValidateUpdateData(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, data *jsonutils.JSONDict) (*jsonutils.JSONDict, error) { - if _, err := route.SStandaloneResourceBase.ValidateUpdateData(ctx, userCred, query, data); err != nil { - return nil, err +func (route *SRoute) ValidateUpdateData(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, input api.RouteUpdateInput) (api.RouteUpdateInput, error) { + var err error + input.StandaloneResourceBaseUpdateInput, err = route.SStandaloneResourceBase.ValidateUpdateData(ctx, userCred, query, input.StandaloneResourceBaseUpdateInput) + if err != nil { + return input, errors.Wrap(err, "SStandaloneResourceBase.ValidateUpdateData") } + data := jsonutils.Marshal(input).(*jsonutils.JSONDict) vs := []validators.IValidator{ validators.NewIPv4PrefixValidator("network"), validators.NewIPv4AddrValidator("gateway"), @@ -147,10 +151,10 @@ func (route *SRoute) ValidateUpdateData(ctx context.Context, userCred mcclient.T for _, v := range vs { v.Optional(true) if err := v.Validate(data); err != nil { - return nil, err + return input, err } } - return nil, nil + return input, nil } func (man *SRouteManager) removeByIface(ctx context.Context, userCred mcclient.TokenCredential, iface *SIface) error { diff --git a/pkg/cloudnet/models/rules.go b/pkg/cloudnet/models/rules.go index b39018ca17..888c225942 100644 --- a/pkg/cloudnet/models/rules.go +++ b/pkg/cloudnet/models/rules.go @@ -24,6 +24,7 @@ import ( "yunion.io/x/sqlchemy" "yunion.io/x/onecloud/pkg/apis" + api "yunion.io/x/onecloud/pkg/apis/cloudnet" "yunion.io/x/onecloud/pkg/cloudcommon/db" "yunion.io/x/onecloud/pkg/cloudcommon/validators" "yunion.io/x/onecloud/pkg/httperrors" @@ -233,14 +234,17 @@ func (man *SRuleManager) ListItemFilter(ctx context.Context, q *sqlchemy.SQuery, return q, nil } -func (rule *SRule) ValidateUpdateData(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, data *jsonutils.JSONDict) (*jsonutils.JSONDict, error) { - if _, err := rule.SStandaloneResourceBase.ValidateUpdateData(ctx, userCred, query, data); err != nil { - return nil, err +func (rule *SRule) ValidateUpdateData(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, input api.RuleUpdateInput) (api.RuleUpdateInput, error) { + var err error + input.StandaloneResourceBaseUpdateInput, err = rule.SStandaloneResourceBase.ValidateUpdateData(ctx, userCred, query, input.StandaloneResourceBaseUpdateInput) + if err != nil { + return input, errors.Wrap(err, "SStandaloneResourceBase.ValidateUpdateData") } + data := jsonutils.Marshal(input).(*jsonutils.JSONDict) if err := RuleManager.validateData(ctx, userCred, rule.GetOwnerId(), query, data, rule); err != nil { - return nil, err + return input, errors.Wrap(err, "validateData") } - return nil, nil + return input, nil } func (man *SRuleManager) removeByRouter(ctx context.Context, userCred mcclient.TokenCredential, router *SRouter) error { diff --git a/pkg/compute/hostdrivers/azure.go b/pkg/compute/hostdrivers/azure.go index 282f0e9ecd..5f11732baa 100644 --- a/pkg/compute/hostdrivers/azure.go +++ b/pkg/compute/hostdrivers/azure.go @@ -45,11 +45,11 @@ func (self *SAzureHostDriver) GetHypervisor() string { return api.HYPERVISOR_AZURE } -func (self *SAzureHostDriver) ValidateUpdateDisk(ctx context.Context, userCred mcclient.TokenCredential, data *jsonutils.JSONDict) (*jsonutils.JSONDict, error) { - if data.Contains("name") { - return nil, httperrors.NewInputParameterError("cannot support change azure disk name") +func (self *SAzureHostDriver) ValidateUpdateDisk(ctx context.Context, userCred mcclient.TokenCredential, input api.DiskUpdateInput) (api.DiskUpdateInput, error) { + if len(input.Name) > 0 { + return input, httperrors.NewInputParameterError("cannot support change azure disk name") } - return data, nil + return input, nil } func (self *SAzureHostDriver) ValidateResetDisk(ctx context.Context, userCred mcclient.TokenCredential, disk *models.SDisk, snapshot *models.SSnapshot, guests []models.SGuest, data *jsonutils.JSONDict) (*jsonutils.JSONDict, error) { diff --git a/pkg/compute/hostdrivers/base.go b/pkg/compute/hostdrivers/base.go index 93815da22c..5bb3d22a4e 100644 --- a/pkg/compute/hostdrivers/base.go +++ b/pkg/compute/hostdrivers/base.go @@ -34,8 +34,8 @@ import ( type SBaseHostDriver struct { } -func (self *SBaseHostDriver) ValidateUpdateDisk(ctx context.Context, userCred mcclient.TokenCredential, data *jsonutils.JSONDict) (*jsonutils.JSONDict, error) { - return data, nil +func (self *SBaseHostDriver) ValidateUpdateDisk(ctx context.Context, userCred mcclient.TokenCredential, input api.DiskUpdateInput) (api.DiskUpdateInput, error) { + return input, nil } func (self *SBaseHostDriver) ValidateResetDisk(ctx context.Context, userCred mcclient.TokenCredential, disk *models.SDisk, snapshot *models.SSnapshot, guests []models.SGuest, data *jsonutils.JSONDict) (*jsonutils.JSONDict, error) { diff --git a/pkg/compute/models/baremetalagents.go b/pkg/compute/models/baremetalagents.go index 510843330a..c5737f4760 100644 --- a/pkg/compute/models/baremetalagents.go +++ b/pkg/compute/models/baremetalagents.go @@ -25,7 +25,6 @@ import ( "yunion.io/x/pkg/util/regutils" "yunion.io/x/sqlchemy" - "yunion.io/x/onecloud/pkg/apis" api "yunion.io/x/onecloud/pkg/apis/compute" "yunion.io/x/onecloud/pkg/cloudcommon/db" "yunion.io/x/onecloud/pkg/httperrors" @@ -80,41 +79,59 @@ func (self *SBaremetalagent) ValidateDeleteCondition(ctx context.Context) error return self.SStandaloneResourceBase.ValidateDeleteCondition(ctx) } -func (self *SBaremetalagent) ValidateUpdateData(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, data *jsonutils.JSONDict) (*jsonutils.JSONDict, error) { - mangerUri, err := data.GetString("manager_uri") - if err == nil { +func (self *SBaremetalagent) ValidateUpdateData(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, input api.BaremetalagentUpdateInput) (api.BaremetalagentUpdateInput, error) { + var err error + mangerUri := input.ManagerUri + if len(mangerUri) > 0 { count, err := BaremetalagentManager.Query().Equals("manager_uri", mangerUri). NotEquals("id", self.Id).CountWithError() if err != nil { - return nil, httperrors.NewInternalServerError("check agent uniqness fail %s", err) + return input, httperrors.NewInternalServerError("check agent uniqness fail %s", err) } if count > 0 { - return nil, httperrors.NewConflictError("Conflict manager_uri %s", mangerUri) + return input, httperrors.NewConflictError("Conflict manager_uri %s", mangerUri) } } - return self.SStandaloneResourceBase.ValidateUpdateData(ctx, userCred, query, data) + if len(input.Zone) > 0 { + zoneObj, err := ValidateZoneResourceInput(userCred, input.ZoneResourceInput) + if err != nil { + return input, errors.Wrap(err, "ValidateZoneResourceInput") + } + input.Zone = zoneObj.GetId() + } + input.StandaloneResourceBaseUpdateInput, err = self.SStandaloneResourceBase.ValidateUpdateData(ctx, userCred, query, input.StandaloneResourceBaseUpdateInput) + if err != nil { + return input, errors.Wrap(err, "SStandaloneResourceBase.ValidateUpdateData") + } + return input, nil } -func (manager *SBaremetalagentManager) ValidateCreateData(ctx context.Context, userCred mcclient.TokenCredential, ownerId mcclient.IIdentityProvider, query jsonutils.JSONObject, data *jsonutils.JSONDict) (*jsonutils.JSONDict, error) { - mangerUri, _ := data.GetString("manager_uri") +func (manager *SBaremetalagentManager) ValidateCreateData(ctx context.Context, userCred mcclient.TokenCredential, ownerId mcclient.IIdentityProvider, query jsonutils.JSONObject, input api.BaremetalagentCreateInput) (api.BaremetalagentCreateInput, error) { + var err error + mangerUri := input.ManagerUri + if len(mangerUri) == 0 { + return input, errors.Wrap(httperrors.ErrMissingParameter, "manager_uri") + } count, err := manager.Query().Equals("manager_uri", mangerUri).CountWithError() if err != nil { - return nil, httperrors.NewInternalServerError("check agent uniqness fail %s", err) + return input, httperrors.NewInternalServerError("check agent uniqness fail %s", err) } if count > 0 { - return nil, httperrors.NewDuplicateResourceError("Duplicate manager_uri %s", mangerUri) + return input, httperrors.NewDuplicateResourceError("Duplicate manager_uri %s", mangerUri) } - input := apis.StandaloneResourceCreateInput{} - err = data.Unmarshal(&input) + if len(input.Zone) == 0 { + return input, errors.Wrap(httperrors.ErrMissingParameter, "zone/zone_id") + } + zoneObj, err := ValidateZoneResourceInput(userCred, input.ZoneResourceInput) if err != nil { - return nil, httperrors.NewInternalServerError("unmarshal StandaloneResourceCreateInput fail %s", err) + return input, errors.Wrap(err, "ValidateZoneResourceInput") } - input, err = manager.SStandaloneResourceBaseManager.ValidateCreateData(ctx, userCred, ownerId, query, input) + input.Zone = zoneObj.GetId() + input.StandaloneResourceCreateInput, err = manager.SStandaloneResourceBaseManager.ValidateCreateData(ctx, userCred, ownerId, query, input.StandaloneResourceCreateInput) if err != nil { - return nil, err + return input, errors.Wrap(err, "SStandaloneResourceBaseManager.ValidateCreateData") } - data.Update(jsonutils.Marshal(input)) - return data, nil + return input, nil } func (self *SBaremetalagent) AllowPerformEnable(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, data jsonutils.JSONObject) bool { diff --git a/pkg/compute/models/buckets.go b/pkg/compute/models/buckets.go index b53cd7aaa3..f6930ebd37 100644 --- a/pkg/compute/models/buckets.go +++ b/pkg/compute/models/buckets.go @@ -486,16 +486,20 @@ func (bucket *SBucket) ValidateUpdateData( ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, - data *jsonutils.JSONDict, -) (*jsonutils.JSONDict, error) { - nameStr, _ := data.GetString("name") - if len(nameStr) > 0 { - err := isValidBucketName(nameStr) + input api.BucketUpdateInput, +) (api.BucketUpdateInput, error) { + var err error + if len(input.Name) > 0 { + err := isValidBucketName(input.Name) if err != nil { - return nil, httperrors.NewInputParameterError("invalid bucket name: %s", err) + return input, httperrors.NewInputParameterError("invalid bucket name(%s): %s", input.Name, err) } } - return bucket.SVirtualResourceBase.ValidateUpdateData(ctx, userCred, query, data) + input.VirtualResourceBaseUpdateInput, err = bucket.SVirtualResourceBase.ValidateUpdateData(ctx, userCred, query, input.VirtualResourceBaseUpdateInput) + if err != nil { + return input, errors.Wrap(err, "SVirtualResourceBase.ValidateUpdateData") + } + return input, nil } func (bucket *SBucket) RemoteCreate(ctx context.Context, userCred mcclient.TokenCredential) error { diff --git a/pkg/compute/models/capabilities.go b/pkg/compute/models/capabilities.go index c2b050e0e6..68108da0e3 100644 --- a/pkg/compute/models/capabilities.go +++ b/pkg/compute/models/capabilities.go @@ -132,10 +132,13 @@ func getRegionZoneSubq(region *SCloudregion) *sqlchemy.SSubQuery { } func domainManagerFieldFilter(domainId, field string) *sqlchemy.SSubQuery { - providers := CloudproviderManager.Query().SubQuery() - accounts := CloudaccountManager.Query().SubQuery() + accounts := CloudaccountManager.Query("id") + accounts = CloudaccountManager.filterByDomainId(accounts, domainId) + accounts = accounts.Equals("status", api.CLOUD_PROVIDER_CONNECTED) + accounts = accounts.IsTrue("enabled") - q := providers.Query(providers.Field(field)) + q := CloudproviderManager.Query(field).Equals("cloudaccount_id", accounts.SubQuery()) + /*q := providers.Query(providers.Field(field)) q = q.Join(accounts, sqlchemy.Equals(accounts.Field("id"), providers.Field("cloudaccount_id"))) q = q.Filter(sqlchemy.OR( sqlchemy.AND( @@ -149,7 +152,7 @@ func domainManagerFieldFilter(domainId, field string) *sqlchemy.SSubQuery { ), )) q = q.Filter(sqlchemy.Equals(accounts.Field("status"), api.CLOUD_PROVIDER_CONNECTED)) - q = q.Filter(sqlchemy.IsTrue(accounts.Field("enabled"))) + q = q.Filter(sqlchemy.IsTrue(accounts.Field("enabled")))*/ return q.SubQuery() } diff --git a/pkg/compute/models/cloudaccounts.go b/pkg/compute/models/cloudaccounts.go index 0255efdf8b..91490def13 100644 --- a/pkg/compute/models/cloudaccounts.go +++ b/pkg/compute/models/cloudaccounts.go @@ -40,7 +40,6 @@ import ( "yunion.io/x/onecloud/pkg/cloudcommon/db/lockman" "yunion.io/x/onecloud/pkg/cloudcommon/db/proxy" "yunion.io/x/onecloud/pkg/cloudcommon/db/taskman" - "yunion.io/x/onecloud/pkg/cloudcommon/policy" "yunion.io/x/onecloud/pkg/cloudcommon/validators" "yunion.io/x/onecloud/pkg/cloudprovider" "yunion.io/x/onecloud/pkg/compute/options" @@ -56,7 +55,7 @@ import ( ) type SCloudaccountManager struct { - db.SEnabledStatusDomainLevelResourceBaseManager + db.SEnabledStatusInfrasResourceBaseManager SSyncableBaseResourceManager } @@ -64,7 +63,7 @@ var CloudaccountManager *SCloudaccountManager func init() { CloudaccountManager = &SCloudaccountManager{ - SEnabledStatusDomainLevelResourceBaseManager: db.NewEnabledStatusDomainLevelResourceBaseManager( + SEnabledStatusInfrasResourceBaseManager: db.NewEnabledStatusInfrasResourceBaseManager( SCloudaccount{}, "cloudaccounts_tbl", "cloudaccount", @@ -77,7 +76,7 @@ func init() { } type SCloudaccount struct { - db.SEnabledStatusDomainLevelResourceBase + db.SEnabledStatusInfrasResourceBase `"public_scope->default":""` SSyncableBaseResource @@ -151,7 +150,7 @@ type SCloudaccount struct { Options *jsonutils.JSONDict `get:"domain" create:"domain_optional" update:"domain"` // for backward compatiblity, keep is_public field, but not usable - IsPublic bool `default:"false" nullable:"false"` + // IsPublic bool `default:"false" nullable:"false"` // add share_mode field to indicate the share range of this account ShareMode string `width:"32" charset:"ascii" nullable:"true" list:"domain"` @@ -213,11 +212,11 @@ func (self *SCloudaccount) ValidateDeleteCondition(ctx context.Context) error { } } - return self.SEnabledStatusDomainLevelResourceBase.ValidateDeleteCondition(ctx) + return self.SEnabledStatusInfrasResourceBase.ValidateDeleteCondition(ctx) } func (self *SCloudaccount) enableAccountOnly(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, input apis.PerformEnableInput) (jsonutils.JSONObject, error) { - return self.SEnabledStatusDomainLevelResourceBase.PerformEnable(ctx, userCred, query, input) + return self.SEnabledStatusInfrasResourceBase.PerformEnable(ctx, userCred, query, input) } func (self *SCloudaccount) PerformEnable(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, input apis.PerformEnableInput) (jsonutils.JSONObject, error) { @@ -241,7 +240,7 @@ func (self *SCloudaccount) PerformEnable(ctx context.Context, userCred mcclient. } func (self *SCloudaccount) PerformDisable(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, input apis.PerformDisableInput) (jsonutils.JSONObject, error) { - _, err := self.SEnabledStatusDomainLevelResourceBase.PerformDisable(ctx, userCred, query, input) + _, err := self.SEnabledStatusInfrasResourceBase.PerformDisable(ctx, userCred, query, input) if err != nil { return nil, err } @@ -263,36 +262,37 @@ func (self *SCloudaccount) PerformDisable(ctx context.Context, userCred mcclient return nil, nil } -func (self *SCloudaccount) ValidateUpdateData(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, data *jsonutils.JSONDict) (*jsonutils.JSONDict, error) { - if data.Contains("sync_interval_seconds") { - syncIntervalSecs, _ := data.Int("sync_interval_seconds") +func (self *SCloudaccount) ValidateUpdateData( + ctx context.Context, + userCred mcclient.TokenCredential, + query jsonutils.JSONObject, + input api.CloudaccountUpdateInput, +) (api.CloudaccountUpdateInput, error) { + var err error + if input.SyncIntervalSeconds != nil { + syncIntervalSecs := *input.SyncIntervalSeconds if syncIntervalSecs == 0 { syncIntervalSecs = int64(options.Options.DefaultSyncIntervalSeconds) } else if syncIntervalSecs < int64(options.Options.MinimalSyncIntervalSeconds) { syncIntervalSecs = int64(options.Options.MinimalSyncIntervalSeconds) } - data.Set("sync_interval_seconds", jsonutils.NewInt(syncIntervalSecs)) + input.SyncIntervalSeconds = &syncIntervalSecs } - if data.Contains("options") || data.Contains("remove_options") { + if (input.Options != nil && input.Options.Length() > 0) || len(input.RemoveOptions) > 0 { var optionsJson *jsonutils.JSONDict if self.Options != nil { - toRemoveKeys, _ := data.GetArray("remove_options") removes := make([]string, 0) - if len(toRemoveKeys) > 0 { - for i := range toRemoveKeys { - key, _ := toRemoveKeys[i].GetString() - removes = append(removes, key) - } + if len(input.RemoveOptions) > 0 { + removes = append(removes, input.RemoveOptions...) } optionsJson = self.Options.CopyExcludes(removes...) } else { optionsJson = jsonutils.NewDict() } - toUpdate, _ := data.Get("options") - if toUpdate != nil { - optionsJson.Update(toUpdate) + if input.Options != nil { + optionsJson.Update(input.Options) } - data.Set("options", optionsJson) + input.Options = optionsJson } v := validators.NewModelIdOrNameValidator( @@ -304,10 +304,21 @@ func (self *SCloudaccount) ValidateUpdateData(ctx context.Context, userCred mccl return nil, err } - return self.SEnabledStatusDomainLevelResourceBase.ValidateUpdateData(ctx, userCred, query, data) + input.EnabledStatusInfrasResourceBaseUpdateInput, err = self.SEnabledStatusInfrasResourceBase.ValidateUpdateData(ctx, userCred, query, input.EnabledStatusInfrasResourceBaseUpdateInput) + if err != nil { + return input, errors.Wrap(err, "SEnabledStatusInfrasResourceBase.ValidateUpdateData") + } + + return input, nil } -func (manager *SCloudaccountManager) ValidateCreateData(ctx context.Context, userCred mcclient.TokenCredential, ownerId mcclient.IIdentityProvider, query jsonutils.JSONObject, input api.CloudaccountCreateInput) (api.CloudaccountCreateInput, error) { +func (manager *SCloudaccountManager) ValidateCreateData( + ctx context.Context, + userCred mcclient.TokenCredential, + ownerId mcclient.IIdentityProvider, + query jsonutils.JSONObject, + input api.CloudaccountCreateInput, +) (api.CloudaccountCreateInput, error) { // check domainId err := db.ValidateCreateDomainId(ownerId.GetProjectDomainId()) if err != nil { @@ -422,7 +433,7 @@ func (manager *SCloudaccountManager) ValidateCreateData(ctx context.Context, use } } - input.EnabledStatusDomainLevelResourceCreateInput, err = manager.SEnabledStatusDomainLevelResourceBaseManager.ValidateCreateData(ctx, userCred, ownerId, query, input.EnabledStatusDomainLevelResourceCreateInput) + input.EnabledStatusInfrasResourceBaseCreateInput, err = manager.SEnabledStatusInfrasResourceBaseManager.ValidateCreateData(ctx, userCred, ownerId, query, input.EnabledStatusInfrasResourceBaseCreateInput) if err != nil { return input, err } @@ -438,11 +449,11 @@ func (self *SCloudaccount) CustomizeCreate(ctx context.Context, userCred mcclien self.DomainId = ownerId.GetProjectDomainId() // self.EnableAutoSync = false self.ShareMode = api.CLOUD_ACCOUNT_SHARE_MODE_ACCOUNT_DOMAIN - return self.SEnabledStatusDomainLevelResourceBase.CustomizeCreate(ctx, userCred, ownerId, query, data) + return self.SEnabledStatusInfrasResourceBase.CustomizeCreate(ctx, userCred, ownerId, query, data) } func (self *SCloudaccount) PostCreate(ctx context.Context, userCred mcclient.TokenCredential, ownerId mcclient.IIdentityProvider, query jsonutils.JSONObject, data jsonutils.JSONObject) { - self.SEnabledStatusDomainLevelResourceBase.PostCreate(ctx, userCred, ownerId, query, data) + self.SEnabledStatusInfrasResourceBase.PostCreate(ctx, userCred, ownerId, query, data) self.savePassword(self.Secret) // if !self.EnableAutoSync { @@ -1038,10 +1049,10 @@ func (manager *SCloudaccountManager) FetchCustomizeColumns( isList bool, ) []api.CloudaccountDetail { rows := make([]api.CloudaccountDetail, len(objs)) - stdRows := manager.SEnabledStatusDomainLevelResourceBaseManager.FetchCustomizeColumns(ctx, userCred, query, objs, fields, isList) + stdRows := manager.SEnabledStatusInfrasResourceBaseManager.FetchCustomizeColumns(ctx, userCred, query, objs, fields, isList) for i := range rows { rows[i] = api.CloudaccountDetail{ - EnabledStatusDomainLevelResourceDetails: stdRows[i], + EnabledStatusInfrasResourceBaseDetails: stdRows[i], } rows[i] = objs[i].(*SCloudaccount).getMoreDetails(rows[i]) } @@ -1163,34 +1174,42 @@ func (manager *SCloudaccountManager) initializeShareMode() error { return nil } -func (manager *SCloudaccountManager) InitializeData() error { - cloudproviders := []SCloudprovider{} - q := CloudproviderManager.Query() - q = q.IsNullOrEmpty("cloudaccount_id") - err := db.FetchModelObjects(CloudproviderManager, q, &cloudproviders) +func (manager *SCloudaccountManager) initializePublicScope() error { + accounts := []SCloudaccount{} + q := manager.Query().IsNullOrEmpty("public_scope") + err := db.FetchModelObjects(manager, q, &accounts) if err != nil { - log.Errorf("fetch all clound provider fail %s", err) + log.Errorf("fetch all clound account fail %v", err) return err } - for i := 0; i < len(cloudproviders); i++ { - err = migrateCloudprovider(&cloudproviders[i]) + for i := 0; i < len(accounts); i++ { + account := &accounts[i] + _, err = db.Update(account, func() error { + switch account.ShareMode { + case api.CLOUD_ACCOUNT_SHARE_MODE_ACCOUNT_DOMAIN: + account.PublicScope = string(rbacutils.ScopeNone) + account.IsPublic = false + case api.CLOUD_ACCOUNT_SHARE_MODE_PROVIDER_DOMAIN: + account.PublicScope = string(rbacutils.ScopeSystem) + account.IsPublic = true + case api.CLOUD_ACCOUNT_SHARE_MODE_SYSTEM: + account.PublicScope = string(rbacutils.ScopeSystem) + account.IsPublic = true + } + return nil + }) if err != nil { return err } } - err = manager.initializeBrand() - if err != nil { - return err - } - err = manager.initializeShareMode() - if err != nil { - return err - } + return nil +} +func (manager *SCloudaccountManager) initializeVMWareAccountId() error { // init accountid - q = manager.Query().Equals("provider", api.CLOUD_PROVIDER_VMWARE) + q := manager.Query().Equals("provider", api.CLOUD_PROVIDER_VMWARE) cloudaccounts := make([]SCloudaccount, 0) - err = db.FetchModelObjects(manager, q, &cloudaccounts) + err := db.FetchModelObjects(manager, q, &cloudaccounts) if err != nil { return errors.Wrap(err, "fetch vmware cloudaccount fail") } @@ -1218,6 +1237,41 @@ func (manager *SCloudaccountManager) InitializeData() error { return nil } +func (manager *SCloudaccountManager) InitializeData() error { + cloudproviders := []SCloudprovider{} + q := CloudproviderManager.Query() + q = q.IsNullOrEmpty("cloudaccount_id") + err := db.FetchModelObjects(CloudproviderManager, q, &cloudproviders) + if err != nil { + log.Errorf("fetch all clound provider fail %s", err) + return err + } + for i := 0; i < len(cloudproviders); i++ { + err = migrateCloudprovider(&cloudproviders[i]) + if err != nil { + return err + } + } + err = manager.initializeBrand() + if err != nil { + return errors.Wrap(err, "initializeBrand") + } + err = manager.initializeShareMode() + if err != nil { + return errors.Wrap(err, "initializeShareMode") + } + err = manager.initializeVMWareAccountId() + if err != nil { + return errors.Wrap(err, "initializeVMWareAccountId") + } + err = manager.initializePublicScope() + if err != nil { + return errors.Wrap(err, "initializePublicScope") + } + + return nil +} + func (self *SCloudaccount) GetBalance() (float64, error) { /*driver, err := self.GetProvider() if err != nil { @@ -1292,19 +1346,72 @@ func (self *SCloudaccount) GetVCenterAccessInfo(privateId string) (SVCenterAcces return info, nil } -func (self *SCloudaccount) AllowPerformChangeProject(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, data jsonutils.JSONObject) bool { +// +onecloud:swagger-gen-ignore +func (account *SCloudaccount) PerformChangeOwner(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, input apis.PerformChangeDomainOwnerInput) (jsonutils.JSONObject, error) { + return nil, errors.Wrap(httperrors.ErrForbidden, "can't change domain owner of cloudaccount, use PerformChangeProject instead") +} + +func (self *SCloudaccount) AllowPerformChangeProject(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, input apis.PerformChangeProjectOwnerInput) bool { return db.IsAdminAllowPerform(userCred, self, "change-project") } -func (self *SCloudaccount) PerformChangeProject(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, data jsonutils.JSONObject) (jsonutils.JSONObject, error) { +func (self *SCloudaccount) PerformChangeProject(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, input apis.PerformChangeProjectOwnerInput) (jsonutils.JSONObject, error) { + if self.IsShared() { + return nil, errors.Wrap(httperrors.ErrInvalidStatus, "cannot change owner when shared!") + } + + project := input.Project + + tenant, err := db.TenantCacheManager.FetchTenantByIdOrName(ctx, project) + if err != nil { + return nil, httperrors.NewNotFoundError("project %s not found", project) + } + + if tenant.Id == self.ProjectId { + return nil, nil + } + providers := self.GetCloudproviders() - if len(providers) > 1 { - return nil, httperrors.NewInvalidStatusError("multiple subaccounts") + if len(providers) > 0 { + for i := range providers { + if providers[i].ProjectId != self.ProjectId { + return nil, errors.Wrap(httperrors.ErrConflict, "cloudproviders' project is different from cloudaccount's") + } + } } - if len(providers) == 0 { - return nil, httperrors.NewInvalidStatusError("no subaccount") + + if tenant.DomainId != self.DomainId { + // do change domainId + input2 := apis.PerformChangeDomainOwnerInput{} + input2.ProjectDomain = tenant.DomainId + _, err := self.SEnabledStatusInfrasResourceBase.PerformChangeOwner(ctx, userCred, query, input2) + if err != nil { + return nil, errors.Wrap(err, "SEnabledStatusInfrasResourceBase.PerformChangeOwner") + } } - return providers[0].PerformChangeProject(ctx, userCred, query, data) + + // save project_id change + diff, err := db.Update(self, func() error { + self.ProjectId = tenant.Id + return nil + }) + + if err != nil { + return nil, errors.Wrap(err, "db.Update ProjectId") + } + + db.OpsLog.LogEvent(self, db.ACT_UPDATE, diff, userCred) + + if len(providers) > 0 { + for i := range providers { + _, err := providers[i].PerformChangeProject(ctx, userCred, query, input) + if err != nil { + return nil, errors.Wrapf(err, "providers[i].PerformChangeProject %s(%s)", providers[i].Name, providers[i].Id) + } + } + } + + return nil, nil } // 云账号列表 @@ -1327,9 +1434,9 @@ func (manager *SCloudaccountManager) ListItemFilter( q = q.Equals("id", accountObj.GetId()) } - q, err := manager.SEnabledStatusDomainLevelResourceBaseManager.ListItemFilter(ctx, q, userCred, query.EnabledStatusDomainLevelResourceListInput) + q, err := manager.SEnabledStatusInfrasResourceBaseManager.ListItemFilter(ctx, q, userCred, query.EnabledStatusInfrasResourceBaseListInput) if err != nil { - return nil, errors.Wrap(err, "SEnabledStatusDomainLevelResourceBaseManager") + return nil, errors.Wrap(err, "SEnabledStatusInfrasResourceBaseManager") } q, err = manager.SSyncableBaseResourceManager.ListItemFilter(ctx, q, userCred, query.SyncableBaseResourceListInput) if err != nil { @@ -1395,7 +1502,7 @@ func (manager *SCloudaccountManager) QueryDistinctExtraField(q *sqlchemy.SQuery, q = q.AppendField(q.Field("name").Label("account")).Distinct() return q, nil } - q, err := manager.SEnabledStatusDomainLevelResourceBaseManager.QueryDistinctExtraField(q, field) + q, err := manager.SEnabledStatusInfrasResourceBaseManager.QueryDistinctExtraField(q, field) if err == nil { return q, nil } @@ -1408,9 +1515,9 @@ func (manager *SCloudaccountManager) OrderByExtraFields( userCred mcclient.TokenCredential, query api.CloudaccountListInput, ) (*sqlchemy.SQuery, error) { - q, err := manager.SEnabledStatusDomainLevelResourceBaseManager.OrderByExtraFields(ctx, q, userCred, query.EnabledStatusDomainLevelResourceListInput) + q, err := manager.SEnabledStatusInfrasResourceBaseManager.OrderByExtraFields(ctx, q, userCred, query.EnabledStatusInfrasResourceBaseListInput) if err != nil { - return nil, errors.Wrap(err, "SEnabledStatusDomainLevelResourceBaseManager.OrderByExtraFields") + return nil, errors.Wrap(err, "SEnabledStatusInfrasResourceBaseManager.OrderByExtraFields") } return q, nil } @@ -1738,7 +1845,7 @@ func (self *SCloudaccount) Delete(ctx context.Context, userCred mcclient.TokenCr func (self *SCloudaccount) RealDelete(ctx context.Context, userCred mcclient.TokenCredential) error { self.SetStatus(userCred, api.CLOUD_PROVIDER_DELETED, "real delete") - return self.SEnabledStatusDomainLevelResourceBase.Delete(ctx, userCred) + return self.SEnabledStatusInfrasResourceBase.Delete(ctx, userCred) } func (self *SCloudaccount) CustomizeDelete(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, data jsonutils.JSONObject) error { @@ -1792,76 +1899,96 @@ func (account *SCloudaccount) setShareMode(userCred mcclient.TokenCredential, mo return nil } -func (account *SCloudaccount) AllowPerformPublic(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, data jsonutils.JSONObject) bool { +func (account *SCloudaccount) AllowPerformPublic(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, input api.CloudaccountPerformPublicInput) bool { return db.IsAllowPerform(rbacutils.ScopeSystem, userCred, account, "public") } -func (account *SCloudaccount) PerformPublic(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, data jsonutils.JSONObject) (jsonutils.JSONObject, error) { - scope := policy.PolicyManager.AllowScope(userCred, consts.GetServiceType(), account.GetModelManager().KeywordPlural(), policy.PolicyActionPerform, "public") - if scope != rbacutils.ScopeSystem { - return nil, httperrors.NewForbiddenError("not enough privilege") +func (account *SCloudaccount) PerformPublic(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, input api.CloudaccountPerformPublicInput) (jsonutils.JSONObject, error) { + if input.ShareMode != api.CLOUD_ACCOUNT_SHARE_MODE_PROVIDER_DOMAIN && input.ShareMode != api.CLOUD_ACCOUNT_SHARE_MODE_SYSTEM { + return nil, errors.Wrap(httperrors.ErrInputParameter, "share_mode cannot be account_domain") } - err := account.setShareMode(userCred, api.CLOUD_ACCOUNT_SHARE_MODE_SYSTEM) + if input.ShareMode == api.CLOUD_ACCOUNT_SHARE_MODE_PROVIDER_DOMAIN { + providers := account.GetCloudproviders() + for i := range providers { + if !utils.IsInStringArray(providers[i].DomainId, input.SharedDomains) { + log.Warningf("provider's domainId %s is outside of list of shared domains", providers[i].DomainId) + input.SharedDomains = append(input.SharedDomains, providers[i].DomainId) + } + } + } + + _, err := account.SInfrasResourceBase.PerformPublic(ctx, userCred, query, input.PerformPublicInput) if err != nil { - return nil, httperrors.NewGeneralError(err) + return nil, errors.Wrap(err, "SInfrasResourceBase.PerformPublic") + } + // scope := policy.PolicyManager.AllowScope(userCred, consts.GetServiceType(), account.GetModelManager().KeywordPlural(), policy.PolicyActionPerform, "public") + // if scope != rbacutils.ScopeSystem { + // return nil, httperrors.NewForbiddenError("not enough privilege") + // } + + err = account.setShareMode(userCred, input.ShareMode) + if err != nil { + return nil, errors.Wrap(err, "account.setShareMode") } return nil, nil } -func (account *SCloudaccount) AllowPerformPrivate(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, data jsonutils.JSONObject) bool { +func (account *SCloudaccount) AllowPerformPrivate(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, input apis.PerformPrivateInput) bool { return db.IsAllowPerform(rbacutils.ScopeSystem, userCred, account, "private") } -func (account *SCloudaccount) PerformPrivate(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, data jsonutils.JSONObject) (jsonutils.JSONObject, error) { +func (account *SCloudaccount) PerformPrivate(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, input apis.PerformPrivateInput) (jsonutils.JSONObject, error) { providers := account.GetCloudproviders() for i := range providers { if providers[i].DomainId != account.DomainId { return nil, httperrors.NewConflictError("provider is shared outside of domain") } } - scope := policy.PolicyManager.AllowScope(userCred, consts.GetServiceType(), account.GetModelManager().KeywordPlural(), policy.PolicyActionPerform, "private") - if scope != rbacutils.ScopeSystem { - return nil, httperrors.NewForbiddenError("not enough privilege") - } - - err := account.setShareMode(userCred, api.CLOUD_ACCOUNT_SHARE_MODE_ACCOUNT_DOMAIN) + _, err := account.SInfrasResourceBase.PerformPrivate(ctx, userCred, query, input) if err != nil { - return nil, httperrors.NewGeneralError(err) + return nil, errors.Wrap(err, "SInfrasResourceBase.PerformPrivate") + } + // scope := policy.PolicyManager.AllowScope(userCred, consts.GetServiceType(), account.GetModelManager().KeywordPlural(), policy.PolicyActionPerform, "private") + // if scope != rbacutils.ScopeSystem { + // return nil, httperrors.NewForbiddenError("not enough privilege") + // } + + err = account.setShareMode(userCred, api.CLOUD_ACCOUNT_SHARE_MODE_ACCOUNT_DOMAIN) + if err != nil { + return nil, errors.Wrap(err, "account.setShareMode") } return nil, nil } -func (account *SCloudaccount) AllowPerformShareMode(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, data jsonutils.JSONObject) bool { +func (account *SCloudaccount) AllowPerformShareMode(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, input api.CloudaccountShareModeInput) bool { return db.IsAllowPerform(rbacutils.ScopeSystem, userCred, account, "share-mode") } -func (account *SCloudaccount) PerformShareMode(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, data jsonutils.JSONObject) (jsonutils.JSONObject, error) { - input := api.CloudaccountShareModeInput{} - err := data.Unmarshal(&input) +func (account *SCloudaccount) PerformShareMode(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, input api.CloudaccountShareModeInput) (jsonutils.JSONObject, error) { + err := input.Validate() if err != nil { - return nil, httperrors.NewInputParameterError("fail to unmarshal input: %s", err) - } - err = input.Validate() - if err != nil { - return nil, err + return nil, errors.Wrap(err, "CloudaccountShareModeInput.Validate") } if account.ShareMode == input.ShareMode { return nil, nil } if input.ShareMode == api.CLOUD_ACCOUNT_SHARE_MODE_ACCOUNT_DOMAIN { - providers := account.GetCloudproviders() - for i := range providers { - if providers[i].DomainId != account.DomainId { - return nil, httperrors.NewConflictError("provider is shared outside of domain") - } + return account.PerformPrivate(ctx, userCred, query, apis.PerformPrivateInput{}) + } else { + input2 := api.CloudaccountPerformPublicInput{ + ShareMode: input.ShareMode, + PerformPublicInput: apis.PerformPublicInput{ + Scope: string(rbacutils.ScopeSystem), + }, } + return account.PerformPublic(ctx, userCred, query, input2) } - scope := policy.PolicyManager.AllowScope(userCred, consts.GetServiceType(), account.GetModelManager().KeywordPlural(), policy.PolicyActionPerform, "share-mode") + /*scope := policy.PolicyManager.AllowScope(userCred, consts.GetServiceType(), account.GetModelManager().KeywordPlural(), policy.PolicyActionPerform, "share-mode") if scope != rbacutils.ScopeSystem { return nil, httperrors.NewForbiddenError("not enough privilege") } @@ -1871,7 +1998,48 @@ func (account *SCloudaccount) PerformShareMode(ctx context.Context, userCred mcc return nil, httperrors.NewGeneralError(err) } - return nil, nil + return nil, nil*/ +} + +func (manager *SCloudaccountManager) filterByDomainId(q *sqlchemy.SQuery, domainId string) *sqlchemy.SQuery { + subq := db.SharedResourceManager.Query("resource_id") + subq = subq.Equals("resource_type", manager.Keyword()) + subq = subq.Equals("target_project_id", domainId) + subq = subq.Equals("target_type", db.SharedTargetDomain) + + cloudproviders := CloudproviderManager.Query().SubQuery() + q = q.LeftJoin(cloudproviders, sqlchemy.Equals( + q.Field("id"), + cloudproviders.Field("cloudaccount_id"), + )) + + q = q.Distinct() + + q = q.Filter(sqlchemy.OR( + // share_mode=account_domain/private + sqlchemy.Equals(q.Field("domain_id"), domainId), + // share_mode=provider_domain/public_scope=domain + // share_mode=provider_domain/public_scope=system + sqlchemy.AND( + sqlchemy.Equals(q.Field("share_mode"), api.CLOUD_ACCOUNT_SHARE_MODE_PROVIDER_DOMAIN), + sqlchemy.Equals(cloudproviders.Field("domain_id"), domainId), + ), + // share_mode=system/public_scope=domain + sqlchemy.AND( + sqlchemy.Equals(q.Field("share_mode"), api.CLOUD_ACCOUNT_SHARE_MODE_SYSTEM), + sqlchemy.In(q.Field("id"), subq.SubQuery()), + sqlchemy.IsTrue(q.Field("is_public")), + sqlchemy.Equals(q.Field("public_scope"), rbacutils.ScopeDomain), + ), + // share_mode=system/public_scope=system + sqlchemy.AND( + sqlchemy.Equals(q.Field("share_mode"), api.CLOUD_ACCOUNT_SHARE_MODE_SYSTEM), + sqlchemy.IsTrue(q.Field("is_public")), + sqlchemy.Equals(q.Field("public_scope"), rbacutils.ScopeSystem), + ), + )) + + return q } func (manager *SCloudaccountManager) FilterByOwner(q *sqlchemy.SQuery, owner mcclient.IIdentityProvider, scope rbacutils.TRbacScope) *sqlchemy.SQuery { @@ -1879,7 +2047,8 @@ func (manager *SCloudaccountManager) FilterByOwner(q *sqlchemy.SQuery, owner mcc switch scope { case rbacutils.ScopeProject, rbacutils.ScopeDomain: if len(owner.GetProjectDomainId()) > 0 { - cloudproviders := CloudproviderManager.Query().SubQuery() + q = manager.filterByDomainId(q, owner.GetProjectDomainId()) + /*cloudproviders := CloudproviderManager.Query().SubQuery() q = q.LeftJoin(cloudproviders, sqlchemy.Equals( q.Field("id"), cloudproviders.Field("cloudaccount_id"), @@ -1895,7 +2064,7 @@ func (manager *SCloudaccountManager) FilterByOwner(q *sqlchemy.SQuery, owner mcc sqlchemy.Equals(q.Field("share_mode"), api.CLOUD_ACCOUNT_SHARE_MODE_PROVIDER_DOMAIN), sqlchemy.Equals(cloudproviders.Field("domain_id"), owner.GetProjectDomainId()), ), - )) + ))*/ } } } @@ -2002,7 +2171,8 @@ func (manager *SCloudaccountManager) queryCloudAccountByCapability(region *SClou q = q.Filter(sqlchemy.Equals(providerregions.Field("cloudregion_id"), region.Id)) } if len(domainId) > 0 { - q = q.Filter(sqlchemy.OR( + q = manager.filterByDomainId(q, domainId) + /*q = q.Filter(sqlchemy.OR( sqlchemy.AND( sqlchemy.Equals(q.Field("share_mode"), api.CLOUD_ACCOUNT_SHARE_MODE_ACCOUNT_DOMAIN), sqlchemy.Equals(q.Field("domain_id"), domainId), @@ -2012,7 +2182,7 @@ func (manager *SCloudaccountManager) queryCloudAccountByCapability(region *SClou sqlchemy.Equals(q.Field("share_mode"), api.CLOUD_ACCOUNT_SHARE_MODE_PROVIDER_DOMAIN), sqlchemy.Equals(providers.Field("domain_id"), domainId), ), - )) + ))*/ } return q } diff --git a/pkg/compute/models/cloudproviders.go b/pkg/compute/models/cloudproviders.go index adac7216a0..31b9756407 100644 --- a/pkg/compute/models/cloudproviders.go +++ b/pkg/compute/models/cloudproviders.go @@ -292,12 +292,18 @@ func (self *SCloudprovider) getSyncRegionCount() (int, error) { return CloudproviderRegionManager.Query().Equals("cloudprovider_id", self.Id).CountWithError() } -func (self *SCloudprovider) ValidateUpdateData(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, data *jsonutils.JSONDict) (*jsonutils.JSONDict, error) { - return self.SEnabledStatusStandaloneResourceBase.ValidateUpdateData(ctx, userCred, query, data) +func (self *SCloudprovider) ValidateUpdateData(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, input api.CloudproviderUpdateInput) (api.CloudproviderUpdateInput, error) { + var err error + input.EnabledStatusStandaloneResourceBaseUpdateInput, err = self.SEnabledStatusStandaloneResourceBase.ValidateUpdateData(ctx, userCred, query, input.EnabledStatusStandaloneResourceBaseUpdateInput) + if err != nil { + return input, errors.Wrap(err, "SEnabledStatusStandaloneResourceBase.ValidateUpdateData") + } + return input, nil } -func (self *SCloudproviderManager) ValidateCreateData(ctx context.Context, userCred mcclient.TokenCredential, ownerId mcclient.IIdentityProvider, query jsonutils.JSONObject, data *jsonutils.JSONDict) (*jsonutils.JSONDict, error) { - return nil, httperrors.NewUnsupportOperationError("Directly creating cloudprovider is not supported, create cloudaccount instead") +// +onecloud:swagger-gen-ignore +func (self *SCloudproviderManager) ValidateCreateData(ctx context.Context, userCred mcclient.TokenCredential, ownerId mcclient.IIdentityProvider, query jsonutils.JSONObject, input api.CloudproviderCreateInput) (api.CloudproviderCreateInput, error) { + return input, httperrors.NewUnsupportOperationError("Directly creating cloudprovider is not supported, create cloudaccount instead") } func (self *SCloudprovider) getAccessUrl() string { @@ -559,15 +565,12 @@ func (self *SCloudprovider) StartSyncCloudProviderInfoTask(ctx context.Context, return nil } -func (self *SCloudprovider) AllowPerformChangeProject(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, data jsonutils.JSONObject) bool { +func (self *SCloudprovider) AllowPerformChangeProject(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, input apis.PerformChangeProjectOwnerInput) bool { return db.IsAdminAllowPerform(userCred, self, "change-project") } -func (self *SCloudprovider) PerformChangeProject(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, data jsonutils.JSONObject) (jsonutils.JSONObject, error) { - project, err := data.GetString("project") - if err != nil { - return nil, httperrors.NewMissingParameterError("project") - } +func (self *SCloudprovider) PerformChangeProject(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, input apis.PerformChangeProjectOwnerInput) (jsonutils.JSONObject, error) { + project := input.Project tenant, err := db.TenantCacheManager.FetchTenantByIdOrName(ctx, project) if err != nil { @@ -583,9 +586,16 @@ func (self *SCloudprovider) PerformChangeProject(ctx context.Context, userCred m if !db.IsAdminAllowPerform(userCred, self, "change-project") { return nil, httperrors.NewForbiddenError("not allow to change project across domain") } - if account.ShareMode == api.CLOUD_ACCOUNT_SHARE_MODE_ACCOUNT_DOMAIN { + if account.ShareMode == api.CLOUD_ACCOUNT_SHARE_MODE_ACCOUNT_DOMAIN && account.DomainId != tenant.DomainId { return nil, httperrors.NewInvalidStatusError("not a public cloud account") } + // if account's public_scope=domain and share_mode=provider_domain, only allow to share to specific domains + if account.PublicScope == string(rbacutils.ScopeDomain) { + sharedDomains := account.GetSharedDomains() + if !utils.IsInStringArray(tenant.DomainId, sharedDomains) && account.DomainId != tenant.DomainId { + return nil, errors.Wrap(httperrors.ErrForbidden, "cannot set to domain outside of the shared domains") + } + } // otherwise, allow change project across domain } diff --git a/pkg/compute/models/cloudsync.go b/pkg/compute/models/cloudsync.go index f6d5f9d92b..414a1fe543 100644 --- a/pkg/compute/models/cloudsync.go +++ b/pkg/compute/models/cloudsync.go @@ -280,7 +280,7 @@ func syncVpcRouteTables(ctx context.Context, userCred mcclient.TokenCredential, log.Errorf(msg) return } - _, _, result := RouteTableManager.SyncRouteTables(ctx, userCred, localVpc, routeTables) + _, _, result := RouteTableManager.SyncRouteTables(ctx, userCred, localVpc, routeTables, provider) syncResults.Add(RouteTableManager, result) @@ -345,7 +345,7 @@ func syncNatDTable(ctx context.Context, userCred mcclient.TokenCredential, provi log.Errorf(msg) return } - result := NatDEntryManager.SyncNatDTable(ctx, userCred, provider.GetOwnerId(), provider, localNatGateway, dtable) + result := NatDEntryManager.SyncNatDTable(ctx, userCred, provider, localNatGateway, dtable) msg := result.Result() log.Infof("SyncNatDTable for NatGateway %s result: %s", localNatGateway.Name, msg) if result.IsError() { @@ -361,7 +361,7 @@ func syncNatSTable(ctx context.Context, userCred mcclient.TokenCredential, provi log.Errorf(msg) return } - result := NatSEntryManager.SyncNatSTable(ctx, userCred, provider.GetOwnerId(), provider, localNatGateway, stable) + result := NatSEntryManager.SyncNatSTable(ctx, userCred, provider, localNatGateway, stable) msg := result.Result() log.Infof("SyncNatSTable for NatGateway %s result: %s", localNatGateway.Name, msg) if result.IsError() { @@ -377,7 +377,7 @@ func syncVpcWires(ctx context.Context, userCred mcclient.TokenCredential, syncRe log.Errorf(msg) return } - localWires, remoteWires, result := WireManager.SyncWires(ctx, userCred, localVpc, wires) + localWires, remoteWires, result := WireManager.SyncWires(ctx, userCred, localVpc, wires, provider) if syncResults != nil { syncResults.Add(WireManager, result) @@ -1212,3 +1212,14 @@ func SyncCloudProject(userCred mcclient.TokenCredential, model db.IVirtualModel, } model.SyncCloudProjectId(userCred, newOwnerId) } + +func SyncCloudDomain(userCred mcclient.TokenCredential, model db.IDomainLevelModel, syncOwnerId mcclient.IIdentityProvider) { + var newOwnerId mcclient.IIdentityProvider + if syncOwnerId != nil && len(syncOwnerId.GetProjectDomainId()) > 0 { + newOwnerId = syncOwnerId + } + if newOwnerId == nil { + newOwnerId = userCred + } + model.SyncCloudDomainId(userCred, newOwnerId) +} diff --git a/pkg/compute/models/disks.go b/pkg/compute/models/disks.go index 7aac93fde6..95357a2257 100644 --- a/pkg/compute/models/disks.go +++ b/pkg/compute/models/disks.go @@ -376,29 +376,36 @@ func (self *SDisk) CustomizeCreate(ctx context.Context, userCred mcclient.TokenC return self.SVirtualResourceBase.CustomizeCreate(ctx, userCred, ownerId, query, data) } -func (self *SDisk) ValidateUpdateData(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, data *jsonutils.JSONDict) (*jsonutils.JSONDict, error) { +func (self *SDisk) ValidateUpdateData(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, input api.DiskUpdateInput) (api.DiskUpdateInput, error) { + var err error + + if input.DiskType != "" { + if !utils.IsInStringArray(input.DiskType, []string{api.DISK_TYPE_DATA, api.DISK_TYPE_VOLUME}) { + return input, httperrors.NewInputParameterError("not support update disk_type %s", input.DiskType) + } + } + storage := self.GetStorage() if storage == nil { - return nil, httperrors.NewNotFoundError("failed to find storage for disk %s", self.Name) + return input, httperrors.NewNotFoundError("failed to find storage for disk %s", self.Name) } host := storage.GetMasterHost() if host == nil { - return nil, httperrors.NewNotFoundError("failed to find host for storage %s with disk %s", storage.Name, self.Name) + return input, httperrors.NewNotFoundError("failed to find host for storage %s with disk %s", storage.Name, self.Name) } - if diskType, _ := data.GetString("disk_type"); diskType != "" { - if !utils.IsInStringArray(diskType, []string{api.DISK_TYPE_DATA, api.DISK_TYPE_VOLUME}) { - return nil, httperrors.NewInputParameterError("not support update disk_type %s", diskType) - } - } - - data, err := host.GetHostDriver().ValidateUpdateDisk(ctx, userCred, data) + input, err = host.GetHostDriver().ValidateUpdateDisk(ctx, userCred, input) if err != nil { - return nil, err + return input, errors.Wrap(err, "GetHostDriver().ValidateUpdateDisk") } - return self.SVirtualResourceBase.ValidateUpdateData(ctx, userCred, query, data) + input.VirtualResourceBaseUpdateInput, err = self.SVirtualResourceBase.ValidateUpdateData(ctx, userCred, query, input.VirtualResourceBaseUpdateInput) + if err != nil { + return input, errors.Wrap(err, "SVirtualResourceBase.ValidateUpdateData") + } + + return input, nil } func diskCreateInput2ComputeQuotaKeys(input api.DiskCreateInput, ownerId mcclient.IIdentityProvider) SComputeResourceKeys { diff --git a/pkg/compute/models/dnsrecords.go b/pkg/compute/models/dnsrecords.go index 18d298432a..ab95b28838 100644 --- a/pkg/compute/models/dnsrecords.go +++ b/pkg/compute/models/dnsrecords.go @@ -419,7 +419,17 @@ func (rec *SDnsRecord) ValidateUpdateData(ctx context.Context, userCred mcclient } data.Set("records", jsonutils.NewString(strings.Join(records, DNS_RECORDS_SEPARATOR))) } - return rec.SAdminSharableVirtualResourceBase.ValidateUpdateData(ctx, userCred, query, data) + input := apis.AdminSharableVirtualResourceBaseUpdateInput{} + err = data.Unmarshal(&input) + if err != nil { + return nil, errors.Wrap(err, "data.Unmarshal AdminSharableVirtualResourceBaseUpdateInput") + } + input, err = rec.SAdminSharableVirtualResourceBase.ValidateUpdateData(ctx, userCred, query, input) + if err != nil { + return nil, errors.Wrap(err, "SAdminSharableVirtualResourceBase.ValidateUpdateData") + } + data.Update(jsonutils.Marshal(input)) + return data, nil } func (rec *SDnsRecord) AddInfo(ctx context.Context, userCred mcclient.TokenCredential, data jsonutils.JSONObject) error { diff --git a/pkg/compute/models/dynamicschedtags.go b/pkg/compute/models/dynamicschedtags.go index e07302c1fb..1d249acf34 100644 --- a/pkg/compute/models/dynamicschedtags.go +++ b/pkg/compute/models/dynamicschedtags.go @@ -167,7 +167,18 @@ func (self *SDynamicschedtag) ValidateUpdateData(ctx context.Context, userCred m return nil, err } - return self.SStandaloneResourceBase.ValidateUpdateData(ctx, userCred, query, data) + input := apis.StandaloneResourceBaseUpdateInput{} + err = data.Unmarshal(&input) + if err != nil { + return nil, errors.Wrap(err, "Unmarshal") + } + input, err = self.SStandaloneResourceBase.ValidateUpdateData(ctx, userCred, query, input) + if err != nil { + return nil, errors.Wrap(err, "SStandaloneResourceBase.ValidateUpdateData") + } + data.Update(jsonutils.Marshal(input)) + + return data, nil } func (self *SDynamicschedtag) GetExtraDetails( diff --git a/pkg/compute/models/globalvpcs.go b/pkg/compute/models/globalvpcs.go index 1c2e826152..2f55c0a03f 100644 --- a/pkg/compute/models/globalvpcs.go +++ b/pkg/compute/models/globalvpcs.go @@ -30,14 +30,14 @@ import ( ) type SGlobalVpcManager struct { - db.SEnabledStatusStandaloneResourceBaseManager + db.SEnabledStatusInfrasResourceBaseManager } var GlobalVpcManager *SGlobalVpcManager func init() { GlobalVpcManager = &SGlobalVpcManager{ - SEnabledStatusStandaloneResourceBaseManager: db.NewEnabledStatusStandaloneResourceBaseManager( + SEnabledStatusInfrasResourceBaseManager: db.NewEnabledStatusInfrasResourceBaseManager( SGlobalVpc{}, "globalvpcs_tbl", "globalvpc", @@ -48,7 +48,7 @@ func init() { } type SGlobalVpc struct { - db.SEnabledStatusStandaloneResourceBase + db.SEnabledStatusInfrasResourceBase } func (manager *SGlobalVpcManager) AllowCreateItem(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, data jsonutils.JSONObject) bool { @@ -63,7 +63,7 @@ func (self *SGlobalVpc) ValidateDeleteCondition(ctx context.Context) error { if len(vpcs) > 0 { return fmt.Errorf("not an empty globalvpc") } - return self.SEnabledStatusStandaloneResourceBase.ValidateDeleteCondition(ctx) + return self.SEnabledStatusInfrasResourceBase.ValidateDeleteCondition(ctx) } func (self *SGlobalVpc) GetVpcs() ([]SVpc, error) { @@ -89,27 +89,43 @@ func (manager *SGlobalVpcManager) FetchCustomizeColumns( isList bool, ) []api.GlobalVpcDetails { rows := make([]api.GlobalVpcDetails, len(objs)) - stdRows := manager.SEnabledStatusStandaloneResourceBaseManager.FetchCustomizeColumns(ctx, userCred, query, objs, fields, isList) + stdRows := manager.SEnabledStatusInfrasResourceBaseManager.FetchCustomizeColumns(ctx, userCred, query, objs, fields, isList) for i := range rows { rows[i] = api.GlobalVpcDetails{ - EnabledStatusStandaloneResourceDetails: stdRows[i], + EnabledStatusInfrasResourceBaseDetails: stdRows[i], } } return rows } -func (manager *SGlobalVpcManager) ValidateCreateData(ctx context.Context, userCred mcclient.TokenCredential, ownerId mcclient.IIdentityProvider, query jsonutils.JSONObject, input api.GlobalVpcCreateInput) (api.GlobalVpcCreateInput, error) { +func (manager *SGlobalVpcManager) ValidateCreateData( + ctx context.Context, + userCred mcclient.TokenCredential, + ownerId mcclient.IIdentityProvider, + query jsonutils.JSONObject, + input api.GlobalVpcCreateInput, +) (api.GlobalVpcCreateInput, error) { input.Status = api.GLOBAL_VPC_STATUS_AVAILABLE var err error - input.EnabledStatusStandaloneResourceCreateInput, err = manager.SEnabledStatusStandaloneResourceBaseManager.ValidateCreateData(ctx, userCred, ownerId, query, input.EnabledStatusStandaloneResourceCreateInput) + input.EnabledStatusInfrasResourceBaseCreateInput, err = manager.SEnabledStatusInfrasResourceBaseManager.ValidateCreateData(ctx, userCred, ownerId, query, input.EnabledStatusInfrasResourceBaseCreateInput) if err != nil { - return input, errors.Wrap(err, "manager.SEnabledStatusStandaloneResourceBaseManager.ValidateCreateData") + return input, errors.Wrap(err, "manager.SEnabledStatusInfrasResourceBaseManager.ValidateCreateData") } return input, nil } -func (self *SGlobalVpc) ValidateUpdateData(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, data *jsonutils.JSONDict) (*jsonutils.JSONDict, error) { - return self.SEnabledStatusStandaloneResourceBase.ValidateUpdateData(ctx, userCred, query, data) +func (self *SGlobalVpc) ValidateUpdateData( + ctx context.Context, + userCred mcclient.TokenCredential, + query jsonutils.JSONObject, + input api.GlobalvpcUpdateInput, +) (api.GlobalvpcUpdateInput, error) { + var err error + input.EnabledStatusInfrasResourceBaseUpdateInput, err = self.SEnabledStatusInfrasResourceBase.ValidateUpdateData(ctx, userCred, query, input.EnabledStatusInfrasResourceBaseUpdateInput) + if err != nil { + return input, errors.Wrap(err, "SEnabledStatusInfrasResourceBase.ValidateUpdateData") + } + return input, nil } // 全局VPC列表 @@ -119,9 +135,9 @@ func (manager *SGlobalVpcManager) ListItemFilter( userCred mcclient.TokenCredential, query api.GlobalVpcListInput, ) (*sqlchemy.SQuery, error) { - q, err := manager.SEnabledStatusStandaloneResourceBaseManager.ListItemFilter(ctx, q, userCred, query.EnabledStatusStandaloneResourceListInput) + q, err := manager.SEnabledStatusInfrasResourceBaseManager.ListItemFilter(ctx, q, userCred, query.EnabledStatusInfrasResourceBaseListInput) if err != nil { - return nil, errors.Wrap(err, "SEnabledStatusStandaloneResourceBaseManager.ListItemFilter") + return nil, errors.Wrap(err, "SEnabledStatusInfrasResourceBaseManager.ListItemFilter") } return q, nil } @@ -132,9 +148,9 @@ func (manager *SGlobalVpcManager) OrderByExtraFields( userCred mcclient.TokenCredential, query api.GlobalVpcListInput, ) (*sqlchemy.SQuery, error) { - q, err := manager.SEnabledStatusStandaloneResourceBaseManager.OrderByExtraFields(ctx, q, userCred, query.EnabledStatusStandaloneResourceListInput) + q, err := manager.SEnabledStatusInfrasResourceBaseManager.OrderByExtraFields(ctx, q, userCred, query.EnabledStatusInfrasResourceBaseListInput) if err != nil { - return nil, errors.Wrap(err, "SEnabledStatusStandaloneResourceBaseManager.OrderByExtraFields") + return nil, errors.Wrap(err, "SEnabledStatusInfrasResourceBaseManager.OrderByExtraFields") } return q, nil } @@ -142,7 +158,7 @@ func (manager *SGlobalVpcManager) OrderByExtraFields( func (manager *SGlobalVpcManager) QueryDistinctExtraField(q *sqlchemy.SQuery, field string) (*sqlchemy.SQuery, error) { var err error - q, err = manager.SEnabledStatusStandaloneResourceBaseManager.QueryDistinctExtraField(q, field) + q, err = manager.SEnabledStatusInfrasResourceBaseManager.QueryDistinctExtraField(q, field) if err == nil { return q, nil } @@ -151,5 +167,5 @@ func (manager *SGlobalVpcManager) QueryDistinctExtraField(q *sqlchemy.SQuery, fi } func (self *SGlobalVpc) ValidateUpdateCondition(ctx context.Context) error { - return self.SEnabledStatusStandaloneResourceBase.ValidateUpdateCondition(ctx) + return self.SEnabledStatusInfrasResourceBase.ValidateUpdateCondition(ctx) } diff --git a/pkg/compute/models/guest_template.go b/pkg/compute/models/guest_template.go index f1070d16b5..2e0415f910 100644 --- a/pkg/compute/models/guest_template.go +++ b/pkg/compute/models/guest_template.go @@ -94,12 +94,13 @@ func (gtm *SGuestTemplateManager) ValidateCreateData( query jsonutils.JSONObject, input computeapis.GuesttemplateCreateInput, ) (computeapis.GuesttemplateCreateInput, error) { + var err error if input.Content == nil { return input, httperrors.NewMissingParameterError("content") } - input, err := gtm.validateData(ctx, userCred, ownerId, query, input) + input.GuesttemplateInput, err = gtm.validateData(ctx, userCred, ownerId, query, input.GuesttemplateInput) if err != nil { return input, errors.Wrap(err, "gtm.validateData") } @@ -128,8 +129,8 @@ func (gtm *SGuestTemplateManager) validateData( userCred mcclient.TokenCredential, ownerId mcclient.IIdentityProvider, query jsonutils.JSONObject, - cinput computeapis.GuesttemplateCreateInput, -) (computeapis.GuesttemplateCreateInput, error) { + cinput computeapis.GuesttemplateInput, +) (computeapis.GuesttemplateInput, error) { if cinput.Content == nil { return cinput, nil } @@ -181,15 +182,22 @@ func (gtm *SGuestTemplateManager) validateData( return cinput, nil } -func (gt *SGuestTemplate) ValidateUpdateData(ctx context.Context, userCred mcclient.TokenCredential, - query jsonutils.JSONObject, cinput *computeapis.GuesttemplateCreateInput) (*jsonutils.JSONDict, error) { - - input, err := GuestTemplateManager.validateData(ctx, userCred, gt.GetOwnerId(), query, *cinput) +func (gt *SGuestTemplate) ValidateUpdateData( + ctx context.Context, + userCred mcclient.TokenCredential, + query jsonutils.JSONObject, + input computeapis.GuesttemplateUpdateInput, +) (computeapis.GuesttemplateUpdateInput, error) { + var err error + input.GuesttemplateInput, err = GuestTemplateManager.validateData(ctx, userCred, gt.GetOwnerId(), query, input.GuesttemplateInput) if err != nil { - return nil, nil + return input, errors.Wrap(err, "GuestTemplateManager.validateData") } - data := input.JSON(input) - return gt.SSharableVirtualResourceBase.ValidateUpdateData(ctx, userCred, query, data) + input.SharableVirtualResourceBaseUpdateInput, err = gt.SSharableVirtualResourceBase.ValidateUpdateData(ctx, userCred, query, input.SharableVirtualResourceBaseUpdateInput) + if err != nil { + return input, errors.Wrap(err, "SSharableVirtualResourceBase.ValidateUpdateData") + } + return input, nil } func (manager *SGuestTemplateManager) FetchCustomizeColumns( @@ -364,8 +372,12 @@ func (gt *SGuestTemplate) getMoreDetails(ctx context.Context, userCred mcclient. return out, nil } -func (gt *SGuestTemplate) PerformPublic(ctx context.Context, userCred mcclient.TokenCredential, - query jsonutils.JSONObject, data apis.PerformProjectPublicInput) (jsonutils.JSONObject, error) { +func (gt *SGuestTemplate) PerformPublic( + ctx context.Context, + userCred mcclient.TokenCredential, + query jsonutils.JSONObject, + data apis.PerformPublicInput, +) (jsonutils.JSONObject, error) { // image, network, secgroup, instancegroup input, err := cmdline.FetchServerCreateInputByJSON(gt.Content) diff --git a/pkg/compute/models/guestdisks.go b/pkg/compute/models/guestdisks.go index 737ceaf2ca..db768341ec 100644 --- a/pkg/compute/models/guestdisks.go +++ b/pkg/compute/models/guestdisks.go @@ -85,24 +85,26 @@ func (self *SGuestdisk) AllowDeleteItem(ctx context.Context, userCred mcclient.T return false } -func (self *SGuestdisk) ValidateUpdateData(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, data *jsonutils.JSONDict) (*jsonutils.JSONDict, error) { - if data.Contains("index") { - if index, err := data.Int("index"); err != nil { - return nil, err - } else { - guestdisk := GuestdiskManager.Query().SubQuery() - count, err := guestdisk.Query().Filter(sqlchemy.Equals(guestdisk.Field("guest_id"), self.GuestId)). - Filter(sqlchemy.NotEquals(guestdisk.Field("disk_id"), self.DiskId)). - Filter(sqlchemy.Equals(guestdisk.Field("index"), index)).CountWithError() - if err != nil { - return nil, httperrors.NewInternalServerError("check disk index uniqueness fail %s", err) - } - if count > 0 { - return nil, httperrors.NewInputParameterError("DISK Index %d has been occupied", index) - } +func (self *SGuestdisk) ValidateUpdateData(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, input api.GuestdiskUpdateInput) (api.GuestdiskUpdateInput, error) { + if input.Index != nil { + index := *input.Index + guestdisk := GuestdiskManager.Query().SubQuery() + count, err := guestdisk.Query().Filter(sqlchemy.Equals(guestdisk.Field("guest_id"), self.GuestId)). + Filter(sqlchemy.NotEquals(guestdisk.Field("disk_id"), self.DiskId)). + Filter(sqlchemy.Equals(guestdisk.Field("index"), index)).CountWithError() + if err != nil { + return input, httperrors.NewInternalServerError("check disk index uniqueness fail %s", err) + } + if count > 0 { + return input, httperrors.NewInputParameterError("DISK Index %d has been occupied", index) } } - return self.SGuestJointsBase.ValidateUpdateData(ctx, userCred, query, data) + var err error + input.GuestJointBaseUpdateInput, err = self.SGuestJointsBase.ValidateUpdateData(ctx, userCred, query, input.GuestJointBaseUpdateInput) + if err != nil { + return input, errors.Wrap(err, "SGuestJointsBase.ValidateUpdateData") + } + return input, nil } func (joint *SGuestdisk) Master() db.IStandaloneModel { diff --git a/pkg/compute/models/guestjoints.go b/pkg/compute/models/guestjoints.go index 6b840a948f..92042f014f 100644 --- a/pkg/compute/models/guestjoints.go +++ b/pkg/compute/models/guestjoints.go @@ -150,3 +150,17 @@ func (manager *SGuestJointsManager) OrderByExtraFields( return q, nil } + +func (self *SGuestJointsBase) ValidateUpdateData( + ctx context.Context, + userCred mcclient.TokenCredential, + query jsonutils.JSONObject, + input api.GuestJointBaseUpdateInput, +) (api.GuestJointBaseUpdateInput, error) { + var err error + input.VirtualJointResourceBaseUpdateInput, err = self.SVirtualJointResourceBase.ValidateUpdateData(ctx, userCred, query, input.VirtualJointResourceBaseUpdateInput) + if err != nil { + return input, errors.Wrap(err, "SVirtualJointResourceBase.ValidateUpdateData") + } + return input, nil +} diff --git a/pkg/compute/models/guestnetworks.go b/pkg/compute/models/guestnetworks.go index 8799bf8790..0f7e00cfd1 100644 --- a/pkg/compute/models/guestnetworks.go +++ b/pkg/compute/models/guestnetworks.go @@ -511,24 +511,31 @@ func (self *SGuestnetwork) GetDetailedString() string { self.MacAddr, network.VlanId, network.Name, self.Driver, self.getBandwidth()) } -func (self *SGuestnetwork) ValidateUpdateData(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, data *jsonutils.JSONDict) (*jsonutils.JSONDict, error) { - if data.Contains("index") { - index, err := data.Int("index") - if err != nil { - return nil, httperrors.NewInternalServerError("fail to fetch index %s", err) - } +func (self *SGuestnetwork) ValidateUpdateData( + ctx context.Context, + userCred mcclient.TokenCredential, + query jsonutils.JSONObject, + input api.GuestnetworkUpdateInput, +) (api.GuestnetworkUpdateInput, error) { + if input.Index != nil { + index := *input.Index q := GuestnetworkManager.Query().SubQuery() count, err := q.Query().Filter(sqlchemy.Equals(q.Field("guest_id"), self.GuestId)). Filter(sqlchemy.NotEquals(q.Field("network_id"), self.NetworkId)). Filter(sqlchemy.Equals(q.Field("index"), index)).CountWithError() if err != nil { - return nil, httperrors.NewInternalServerError("checkout nic index uniqueness fail %s", err) + return input, httperrors.NewInternalServerError("checkout nic index uniqueness fail %s", err) } if count > 0 { - return nil, httperrors.NewDuplicateResourceError("NIC Index %d has been occupied", index) + return input, httperrors.NewDuplicateResourceError("NIC Index %d has been occupied", index) } } - return self.SJointResourceBase.ValidateUpdateData(ctx, userCred, query, data) + var err error + input.GuestJointBaseUpdateInput, err = self.SGuestJointsBase.ValidateUpdateData(ctx, userCred, query, input.GuestJointBaseUpdateInput) + if err != nil { + return input, errors.Wrap(err, "SGuestJointsBase.ValidateUpdateData") + } + return input, nil } func (manager *SGuestnetworkManager) DeleteGuestNics(ctx context.Context, userCred mcclient.TokenCredential, gns []SGuestnetwork, reserve bool) error { diff --git a/pkg/compute/models/guests.go b/pkg/compute/models/guests.go index 956d375703..958f6819de 100644 --- a/pkg/compute/models/guests.go +++ b/pkg/compute/models/guests.go @@ -23,6 +23,8 @@ import ( "strings" "time" + "yunion.io/x/onecloud/pkg/apis" + "yunion.io/x/jsonutils" "yunion.io/x/log" "yunion.io/x/pkg/errors" @@ -863,7 +865,17 @@ func (self *SGuest) ValidateUpdateData(ctx context.Context, userCred mcclient.To return nil, httperrors.NewInputParameterError("name is too short") } } - return self.SVirtualResourceBase.ValidateUpdateData(ctx, userCred, query, data) + input := apis.VirtualResourceBaseUpdateInput{} + err = data.Unmarshal(&input) + if err != nil { + return nil, errors.Wrap(err, "data.Unmarshal") + } + input, err = self.SVirtualResourceBase.ValidateUpdateData(ctx, userCred, query, input) + if err != nil { + return nil, errors.Wrap(err, "SVirtualResourceBase.ValidateUpdateData") + } + data.Update(jsonutils.Marshal(input)) + return data, nil } func serverCreateInput2ComputeQuotaKeys(input api.ServerCreateInput, ownerId mcclient.IIdentityProvider) SComputeResourceKeys { diff --git a/pkg/compute/models/hostdrivers.go b/pkg/compute/models/hostdrivers.go index 38d5838539..69023489f6 100644 --- a/pkg/compute/models/hostdrivers.go +++ b/pkg/compute/models/hostdrivers.go @@ -32,7 +32,7 @@ type IHostDriver interface { CheckAndSetCacheImage(ctx context.Context, host *SHost, storagecache *SStoragecache, task taskman.ITask) error RequestUncacheImage(ctx context.Context, host *SHost, storageCache *SStoragecache, task taskman.ITask) error - ValidateUpdateDisk(ctx context.Context, userCred mcclient.TokenCredential, data *jsonutils.JSONDict) (*jsonutils.JSONDict, error) + ValidateUpdateDisk(ctx context.Context, userCred mcclient.TokenCredential, input api.DiskUpdateInput) (api.DiskUpdateInput, error) ValidateResetDisk(ctx context.Context, userCred mcclient.TokenCredential, disk *SDisk, snapshot *SSnapshot, guests []SGuest, data *jsonutils.JSONDict) (*jsonutils.JSONDict, error) ValidateDiskSize(storage *SStorage, sizeGb int) error RequestPrepareSaveDiskOnHost(ctx context.Context, host *SHost, disk *SDisk, imageId string, task taskman.ITask) error diff --git a/pkg/compute/models/hosts.go b/pkg/compute/models/hosts.go index 4dbbac47ee..2b77a0d318 100644 --- a/pkg/compute/models/hosts.go +++ b/pkg/compute/models/hosts.go @@ -25,6 +25,8 @@ import ( "strings" "time" + "yunion.io/x/onecloud/pkg/util/rbacutils" + "yunion.io/x/jsonutils" "yunion.io/x/log" "yunion.io/x/pkg/errors" @@ -58,7 +60,7 @@ import ( ) type SHostManager struct { - db.SEnabledStatusStandaloneResourceBaseManager + db.SEnabledStatusInfrasResourceBaseManager db.SExternalizedResourceBaseManager SZoneResourceBaseManager SManagedResourceBaseManager @@ -68,7 +70,7 @@ var HostManager *SHostManager func init() { HostManager = &SHostManager{ - SEnabledStatusStandaloneResourceBaseManager: db.NewEnabledStatusStandaloneResourceBaseManager( + SEnabledStatusInfrasResourceBaseManager: db.NewEnabledStatusInfrasResourceBaseManager( SHost{}, "hosts_tbl", "host", @@ -80,7 +82,7 @@ func init() { } type SHost struct { - db.SEnabledStatusStandaloneResourceBase + db.SEnabledStatusInfrasResourceBase db.SExternalizedResourceBase SZoneResourceBase SManagedResourceBase @@ -227,9 +229,9 @@ func (manager *SHostManager) ListItemFilter( } } - q, err = manager.SEnabledStatusStandaloneResourceBaseManager.ListItemFilter(ctx, q, userCred, query.EnabledStatusStandaloneResourceListInput) + q, err = manager.SEnabledStatusInfrasResourceBaseManager.ListItemFilter(ctx, q, userCred, query.EnabledStatusInfrasResourceBaseListInput) if err != nil { - return nil, errors.Wrap(err, "SEnabledStatusStandaloneResourceBaseManager.ListItemFilter") + return nil, errors.Wrap(err, "SEnabledStatusInfrasResourceBaseManager.ListItemFilter") } anyMac := query.AnyMac @@ -436,9 +438,9 @@ func (manager *SHostManager) OrderByExtraFields( query api.HostListInput, ) (*sqlchemy.SQuery, error) { var err error - q, err = manager.SEnabledStatusStandaloneResourceBaseManager.OrderByExtraFields(ctx, q, userCred, query.EnabledStatusStandaloneResourceListInput) + q, err = manager.SEnabledStatusInfrasResourceBaseManager.OrderByExtraFields(ctx, q, userCred, query.EnabledStatusInfrasResourceBaseListInput) if err != nil { - return nil, errors.Wrap(err, "SEnabledStatusStandaloneResourceBaseManager.OrderByExtraFields") + return nil, errors.Wrap(err, "SEnabledStatusInfrasResourceBaseManager.OrderByExtraFields") } q, err = manager.SManagedResourceBaseManager.OrderByExtraFields(ctx, q, userCred, query.ManagedResourceListInput) if err != nil { @@ -453,7 +455,7 @@ func (manager *SHostManager) OrderByExtraFields( func (manager *SHostManager) QueryDistinctExtraField(q *sqlchemy.SQuery, field string) (*sqlchemy.SQuery, error) { var err error - q, err = manager.SEnabledStatusStandaloneResourceBaseManager.QueryDistinctExtraField(q, field) + q, err = manager.SEnabledStatusInfrasResourceBaseManager.QueryDistinctExtraField(q, field) if err == nil { return q, nil } @@ -610,7 +612,7 @@ func (self *SHost) validateDeleteCondition(ctx context.Context, purge bool) erro } } - return self.SEnabledStatusStandaloneResourceBase.ValidateDeleteCondition(ctx) + return self.SEnabledStatusInfrasResourceBase.ValidateDeleteCondition(ctx) } func (self *SHost) Delete(ctx context.Context, userCred mcclient.TokenCredential) error { @@ -677,7 +679,7 @@ func (self *SHost) RealDelete(ctx context.Context, userCred mcclient.TokenCreden store.Delete(ctx, userCred) } } - return self.SEnabledStatusStandaloneResourceBase.Delete(ctx, userCred) + return self.SEnabledStatusInfrasResourceBase.Delete(ctx, userCred) } func (self *SHost) GetHoststoragesQuery() *sqlchemy.SQuery { @@ -1183,6 +1185,18 @@ func (self *SHost) GetAttachedLocalStorageCapacity() SStorageCapacity { return ret } +func (self *SHost) GetAttachedLocalStorages() []SStorage { + ret := make([]SStorage, 0) + storages := self.GetAttachedStorages("") + for _, s := range storages { + if !utils.IsInStringArray(s.StorageType, api.HOST_STORAGE_LOCAL_TYPES) { + continue + } + ret = append(ret, s) + } + return ret +} + func _getLeastUsedStorage(storages []SStorage, backends []string) *SStorage { var best *SStorage var bestCap int64 @@ -1505,7 +1519,7 @@ func (manager *SHostManager) SyncHosts(ctx context.Context, userCred mcclient.To } } for i := 0; i < len(commondb); i += 1 { - err = commondb[i].syncWithCloudHost(ctx, userCred, commonext[i]) + err = commondb[i].syncWithCloudHost(ctx, userCred, commonext[i], provider.GetOwnerId()) if err != nil { syncResult.UpdateError(err) } else { @@ -1551,7 +1565,7 @@ func (self *SHost) syncRemoveCloudHost(ctx context.Context, userCred mcclient.To return err } -func (self *SHost) syncWithCloudHost(ctx context.Context, userCred mcclient.TokenCredential, extHost cloudprovider.ICloudHost) error { +func (self *SHost) syncWithCloudHost(ctx context.Context, userCred mcclient.TokenCredential, extHost cloudprovider.ICloudHost, syncOwnerId mcclient.IIdentityProvider) error { diff, err := db.UpdateWithLock(ctx, self, func() error { // self.Name = extHost.GetName() @@ -1597,6 +1611,8 @@ func (self *SHost) syncWithCloudHost(ctx context.Context, userCred mcclient.Toke db.OpsLog.LogSyncUpdate(self, diff, userCred) + SyncCloudDomain(userCred, self, syncOwnerId) + if err := HostManager.ClearSchedDescCache(self.Id); err != nil { log.Errorf("ClearSchedDescCache for host %s error %v", self.Name, err) } @@ -1693,6 +1709,9 @@ func (manager *SHostManager) newFromCloudHost(ctx context.Context, userCred mccl host.IsMaintenance = extHost.GetIsMaintenance() host.Version = extHost.GetVersion() + host.IsPublic = false + host.PublicScope = string(rbacutils.ScopeNone) + err = manager.TableSpec().Insert(&host) if err != nil { log.Errorf("newFromCloudHost fail %s", err) @@ -1701,6 +1720,8 @@ func (manager *SHostManager) newFromCloudHost(ctx context.Context, userCred mccl db.OpsLog.LogEvent(&host, db.ACT_CREATE, host.GetShortDesc(ctx), userCred) + SyncCloudDomain(userCred, &host, provider.GetOwnerId()) + if err := manager.ClearSchedDescCache(host.Id); err != nil { log.Errorf("ClearSchedDescCache for host %s error %v", host.Name, err) } @@ -1802,7 +1823,7 @@ func (self *SHost) syncWithCloudHostStorage(ctx context.Context, userCred mcclie return err } s := hs.GetStorage() - err = s.syncWithCloudStorage(ctx, userCred, extStorage) + err = s.syncWithCloudStorage(ctx, userCred, extStorage, nil) return err } @@ -2550,12 +2571,12 @@ func (manager *SHostManager) FetchCustomizeColumns( isList bool, ) []api.HostDetails { rows := make([]api.HostDetails, len(objs)) - stdRows := manager.SEnabledStatusStandaloneResourceBaseManager.FetchCustomizeColumns(ctx, userCred, query, objs, fields, isList) + stdRows := manager.SEnabledStatusInfrasResourceBaseManager.FetchCustomizeColumns(ctx, userCred, query, objs, fields, isList) managerRows := manager.SManagedResourceBaseManager.FetchCustomizeColumns(ctx, userCred, query, objs, fields, isList) zoneRows := manager.SZoneResourceBaseManager.FetchCustomizeColumns(ctx, userCred, query, objs, fields, isList) for i := range rows { rows[i] = api.HostDetails{ - EnabledStatusStandaloneResourceDetails: stdRows[i], + EnabledStatusInfrasResourceBaseDetails: stdRows[i], ManagedResourceInfo: managerRows[i], ZoneResourceInfo: zoneRows[i], } @@ -2642,37 +2663,50 @@ func (self *SHost) GetStoragecache() *SStoragecache { return nil } -func (self *SHost) PostCreate(ctx context.Context, userCred mcclient.TokenCredential, ownerId mcclient.IIdentityProvider, query jsonutils.JSONObject, data jsonutils.JSONObject) { - self.SEnabledStatusStandaloneResourceBase.PostCreate(ctx, userCred, ownerId, query, data) - kwargs := data.(*jsonutils.JSONDict) - ipmiInfo, err := fetchIpmiInfo(kwargs, self.Id) +func (self *SHost) PostCreate( + ctx context.Context, + userCred mcclient.TokenCredential, + ownerId mcclient.IIdentityProvider, + query jsonutils.JSONObject, + data jsonutils.JSONObject, +) { + self.SEnabledStatusInfrasResourceBase.PostCreate(ctx, userCred, ownerId, query, data) + input := api.HostCreateInput{} + err := data.Unmarshal(&input) if err != nil { - log.Errorln(err.Error()) - } else if ipmiInfo.Length() > 0 { + log.Errorf("data.Unmarshal fail %s", err) + return + } + kwargs := data.(*jsonutils.JSONDict) + ipmiInfo, err := fetchIpmiInfo(input.HostIpmiAttributes, self.Id) + if err != nil { + log.Errorf("fetchIpmiInfo fail %s", err) + return + } + ipmiInfoJson := jsonutils.Marshal(ipmiInfo).(*jsonutils.JSONDict) + if ipmiInfoJson.Length() > 0 { _, err := self.SaveUpdates(func() error { - self.IpmiInfo = ipmiInfo + self.IpmiInfo = ipmiInfoJson return nil }) if err != nil { log.Errorln(err.Error()) - } else { - ipmiIp, _ := ipmiInfo.GetString("ip_addr") - if len(ipmiIp) > 0 { - self.setIpmiIp(userCred, ipmiIp) - } + } else if len(ipmiInfo.IpAddr) > 0 { + self.setIpmiIp(userCred, ipmiInfo.IpAddr) } } - accessIp, _ := data.GetString("access_ip") - if len(accessIp) > 0 { - self.setAccessIp(userCred, accessIp) + if len(input.AccessIp) > 0 { + self.setAccessIp(userCred, input.AccessIp) } - accessMac, _ := data.GetString("access_mac") - if len(accessMac) > 0 { - self.setAccessMac(userCred, accessMac) + if len(input.AccessMac) > 0 { + self.setAccessMac(userCred, input.AccessMac) + } + noProbe := false + if input.NoProbe != nil { + noProbe = *input.NoProbe } - noProbe := jsonutils.QueryBoolean(data, "no_probe", false) if len(self.ZoneId) > 0 && self.HostType == api.HOST_TYPE_BAREMETAL && !noProbe { - ipmiInfo, _ := self.GetIpmiInfo() + // ipmiInfo, _ := self.GetIpmiInfo() if len(ipmiInfo.IpAddr) > 0 { self.StartBaremetalCreateTask(ctx, userCred, kwargs, "") } @@ -2689,49 +2723,51 @@ func (self *SHost) StartBaremetalCreateTask(ctx context.Context, userCred mcclie } } -func (manager *SHostManager) ValidateSizeParams(data *jsonutils.JSONDict) (*jsonutils.JSONDict, error) { - memStr, _ := data.GetString("mem_size") +func (manager *SHostManager) ValidateSizeParams(input api.HostSizeAttributes) (api.HostSizeAttributes, error) { + memStr := input.MemSize if len(memStr) > 0 { if !regutils.MatchSize(memStr) { - return nil, fmt.Errorf("Memory size must be number[+unit], like 256M, 1G or 256") + return input, errors.Wrap(httperrors.ErrInputParameter, "Memory size must be number[+unit], like 256M, 1G or 256") } memSize, err := fileutils.GetSizeMb(memStr, 'M', 1024) if err != nil { - return nil, err + return input, errors.Wrap(err, "fileutils.GetSizeMb") } - data.Set("mem_size", jsonutils.NewInt(int64(memSize))) + input.MemSize = strconv.FormatInt(int64(memSize), 10) + // data.Set("mem_size", jsonutils.NewInt(int64(memSize))) } - memReservedStr, _ := data.GetString("mem_reserved") + memReservedStr := input.MemReserved if len(memReservedStr) > 0 { if !regutils.MatchSize(memReservedStr) { - return nil, fmt.Errorf("Memory size must be number[+unit], like 256M, 1G or 256") + return input, errors.Wrap(httperrors.ErrInputParameter, "Memory size must be number[+unit], like 256M, 1G or 256") } memSize, err := fileutils.GetSizeMb(memReservedStr, 'M', 1024) if err != nil { - return nil, err + return input, errors.Wrap(err, "fileutils.GetSizeMb") } - data.Set("mem_reserved", jsonutils.NewInt(int64(memSize))) + input.MemReserved = strconv.FormatInt(int64(memSize), 10) + // data.Set("mem_reserved", jsonutils.NewInt(int64(memSize))) } - cpuCacheStr, _ := data.GetString("cpu_cache") + cpuCacheStr := input.CpuCache if len(cpuCacheStr) > 0 { if !regutils.MatchSize(cpuCacheStr) { - return nil, fmt.Errorf("Illegal cpu cache size %s", cpuCacheStr) + return input, errors.Wrapf(httperrors.ErrInputParameter, "Illegal cpu cache size %s", cpuCacheStr) } cpuCache, err := fileutils.GetSizeKb(cpuCacheStr, 'K', 1024) if err != nil { - return nil, err + return input, errors.Wrap(err, "fileutils.GetSizeKb") } - data.Set("cpu_cache", jsonutils.NewInt(int64(cpuCache))) + input.CpuCache = strconv.FormatInt(int64(cpuCache), 10) + // data.Set("cpu_cache", jsonutils.NewInt(int64(cpuCache))) } - return data, nil + return input, nil } -func (manager *SHostManager) inputUniquenessCheck(data *jsonutils.JSONDict, zoneId string, hostId string) (*jsonutils.JSONDict, error) { - for _, key := range []string{ - "manager_uri", - "access_ip", +func (manager *SHostManager) inputUniquenessCheck(input api.HostAccessAttributes, zoneId string, hostId string) (api.HostAccessAttributes, error) { + for key, val := range map[string]string{ + "manager_uri": input.ManagerUri, + "access_ip": input.AccessIp, } { - val, _ := data.GetString(key) if len(val) > 0 { q := manager.Query().Equals(key, val) if len(zoneId) > 0 { @@ -2744,19 +2780,19 @@ func (manager *SHostManager) inputUniquenessCheck(data *jsonutils.JSONDict, zone } cnt, err := q.CountWithError() if err != nil { - return nil, httperrors.NewInternalServerError("check %s duplication fail %s", key, err) + return input, httperrors.NewInternalServerError("check %s duplication fail %s", key, err) } if cnt > 0 { - return nil, httperrors.NewConflictError("duplicate %s %s", key, val) + return input, httperrors.NewConflictError("duplicate %s %s", key, val) } } } - accessMac, _ := data.GetString("access_mac") + accessMac := input.AccessMac if len(accessMac) > 0 { accessMac2 := netutils.FormatMacAddr(accessMac) if len(accessMac2) == 0 { - return nil, httperrors.NewInputParameterError("invalid macAddr %s", accessMac) + return input, httperrors.NewInputParameterError("invalid macAddr %s", accessMac) } if accessMac2 != api.ACCESS_MAC_ANY { q := manager.Query().Equals("access_mac", accessMac2) @@ -2765,71 +2801,76 @@ func (manager *SHostManager) inputUniquenessCheck(data *jsonutils.JSONDict, zone } cnt, err := q.CountWithError() if err != nil { - return nil, httperrors.NewInternalServerError("check access_mac duplication fail %s", err) + return input, httperrors.NewInternalServerError("check access_mac duplication fail %s", err) } if cnt > 0 { - return nil, httperrors.NewConflictError("duplicate access_mac %s", accessMac) + return input, httperrors.NewConflictError("duplicate access_mac %s", accessMac) } - data.Set("access_mac", jsonutils.NewString(accessMac2)) + input.AccessMac = accessMac2 } } - return data, nil + return input, nil } -func (manager *SHostManager) ValidateCreateData(ctx context.Context, userCred mcclient.TokenCredential, ownerId mcclient.IIdentityProvider, query jsonutils.JSONObject, data *jsonutils.JSONDict) (*jsonutils.JSONDict, error) { - zoneId, zoneKey := jsonutils.GetAnyString2(data, []string{"zone_id", "zone"}) - if len(zoneId) > 0 { - data.Remove(zoneKey) - zoneObj, err := ZoneManager.FetchByIdOrName(userCred, zoneId) +func (manager *SHostManager) ValidateCreateData( + ctx context.Context, + userCred mcclient.TokenCredential, + ownerId mcclient.IIdentityProvider, + query jsonutils.JSONObject, + input api.HostCreateInput, +) (api.HostCreateInput, error) { + var err error + + if len(input.Zone) > 0 { + zoneObj, err := ValidateZoneResourceInput(userCred, input.ZoneResourceInput) if err != nil { - if err == sql.ErrNoRows { - return nil, httperrors.NewResourceNotFoundError2(ZoneManager.Keyword(), zoneId) - } else { - return nil, httperrors.NewGeneralError(err) - } + return input, errors.Wrap(err, "ValidateZoneResourceInput") } - zoneId = zoneObj.GetId() - data.Set("zone_id", jsonutils.NewString(zoneObj.GetId())) + input.Zone = zoneObj.GetId() } - noProbe := jsonutils.QueryBoolean(data, "no_probe", false) - - data, err := manager.inputUniquenessCheck(data, zoneId, "") - if err != nil { - return nil, err + noProbe := false + if input.NoProbe != nil { + noProbe = *input.NoProbe } - data, err = manager.ValidateSizeParams(data) + input.HostAccessAttributes, err = manager.inputUniquenessCheck(input.HostAccessAttributes, input.Zone, "") if err != nil { - return nil, httperrors.NewInputParameterError(err.Error()) + return input, errors.Wrap(err, "manager.inputUniquenessCheck") } - memReserved, err := data.Int("mem_reserved") + + input.HostSizeAttributes, err = manager.ValidateSizeParams(input.HostSizeAttributes) if err != nil { - hostType, _ := data.GetString("host_type") - if hostType != api.HOST_TYPE_BAREMETAL { - memSize, _ := data.Int("mem_size") - memReserved = memSize / 8 + return input, errors.Wrap(err, "manager.ValidateSizeParams") + } + + if len(input.MemReserved) == 0 { + if input.HostType != api.HOST_TYPE_BAREMETAL { + memSize, _ := strconv.ParseInt(input.MemSize, 10, 64) + memReserved := memSize / 8 if memReserved > 4096 { memReserved = 4096 } - data.Set("mem_reserved", jsonutils.NewInt(memReserved)) + input.MemReserved = strconv.FormatInt(memReserved, 10) + // data.Set("mem_reserved", jsonutils.NewInt(memReserved)) } else { - data.Set("mem_reserved", jsonutils.NewInt(0)) + input.MemReserved = "0" + // data.Set("mem_reserved", jsonutils.NewInt(0)) } } - ipmiInfo, err := fetchIpmiInfo(data, "") + + ipmiInfo, err := fetchIpmiInfo(input.HostIpmiAttributes, "") if err != nil { - log.Errorln(err.Error()) - return nil, httperrors.NewInputParameterError("%s", err) + return input, errors.Wrap(err, "fetchIpmiInfo") } - ipmiIpAddr, _ := ipmiInfo.GetString("ip_addr") + ipmiIpAddr := ipmiInfo.IpAddr if len(ipmiIpAddr) == 0 { noProbe = true } if len(ipmiIpAddr) > 0 && !noProbe { net, _ := NetworkManager.GetOnPremiseNetworkOfIP(ipmiIpAddr, "", tristate.None) if net == nil { - return nil, httperrors.NewInputParameterError("%s is out of network IP ranges", ipmiIpAddr) + return input, httperrors.NewInputParameterError("%s is out of network IP ranges", ipmiIpAddr) } // check ip has been reserved rip := ReservedipManager.GetReservedIP(net, ipmiIpAddr) @@ -2837,49 +2878,50 @@ func (manager *SHostManager) ValidateCreateData(ctx context.Context, userCred mc // if not, reserve this IP temporarily err := net.reserveIpWithDuration(ctx, userCred, ipmiIpAddr, "reserve for baremetal ipmi IP", 30*time.Minute) if err != nil { - return nil, errors.Wrap(err, "net.reserveIpWithDuration") + return input, errors.Wrap(err, "net.reserveIpWithDuration") } } zoneObj := net.GetZone() if zoneObj == nil { - return nil, httperrors.NewInputParameterError("IPMI network has no zone???") + return input, httperrors.NewInputParameterError("IPMI network has no zone???") } - originZoneId, _ := data.GetString("zone_id") + originZoneId := input.Zone if len(originZoneId) > 0 && originZoneId != zoneObj.GetId() { - return nil, httperrors.NewInputParameterError("IPMI address located in different zone than specified") + return input, httperrors.NewInputParameterError("IPMI address located in different zone than specified") } - data.Set("zone_id", jsonutils.NewString(zoneObj.GetId())) + input.Zone = zoneObj.GetId() + // data.Set("zone_id", jsonutils.NewString(zoneObj.GetId())) } if !noProbe { var accessNet *SNetwork - accessIpAddr, _ := data.GetString("access_ip") + accessIpAddr := input.AccessIp // tString("access_ip") if len(accessIpAddr) > 0 { net, _ := NetworkManager.GetOnPremiseNetworkOfIP(accessIpAddr, "", tristate.None) if net == nil { - return nil, httperrors.NewInputParameterError("%s is out of network IP ranges", accessIpAddr) + return input, httperrors.NewInputParameterError("%s is out of network IP ranges", accessIpAddr) } accessNet = net } else { - accessNetStr, _ := data.GetString("access_net") + accessNetStr := input.AccessNet // data.GetString("access_net") if len(accessNetStr) > 0 { netObj, err := NetworkManager.FetchByIdOrName(userCred, accessNetStr) if err != nil { if errors.Cause(err) == sql.ErrNoRows { - return nil, httperrors.NewResourceNotFoundError2("network", accessNetStr) + return input, httperrors.NewResourceNotFoundError2("network", accessNetStr) } else { - return nil, httperrors.NewGeneralError(err) + return input, httperrors.NewGeneralError(err) } } accessNet = netObj.(*SNetwork) } else { - accessWireStr, _ := data.GetString("access_wire") + accessWireStr := input.AccessWire // data.GetString("access_wire") if len(accessWireStr) > 0 { wireObj, err := WireManager.FetchByIdOrName(userCred, accessWireStr) if err != nil { if errors.Cause(err) == sql.ErrNoRows { - return nil, httperrors.NewResourceNotFoundError2("wire", accessWireStr) + return input, httperrors.NewResourceNotFoundError2("wire", accessWireStr) } else { - return nil, httperrors.NewGeneralError(err) + return input, httperrors.NewGeneralError(err) } } wire := wireObj.(*SWire) @@ -2887,7 +2929,7 @@ func (manager *SHostManager) ValidateCreateData(ctx context.Context, userCred mc defer lockman.ReleaseObject(ctx, wire) net, err := wire.GetCandidatePrivateNetwork(userCred, false, []string{api.NETWORK_TYPE_PXE, api.NETWORK_TYPE_BAREMETAL, api.NETWORK_TYPE_GUEST}) if err != nil { - return nil, httperrors.NewGeneralError(err) + return input, httperrors.NewGeneralError(err) } accessNet = net } @@ -2899,20 +2941,20 @@ func (manager *SHostManager) ValidateCreateData(ctx context.Context, userCred mc accessIp, err := accessNet.GetFreeIP(ctx, userCred, nil, nil, accessIpAddr, api.IPAllocationNone, true) if err != nil { - return nil, httperrors.NewGeneralError(err) + return input, httperrors.NewGeneralError(err) } if len(accessIpAddr) > 0 && accessIpAddr != accessIp { - return nil, httperrors.NewConflictError("Access ip %s has been used", accessIpAddr) + return input, httperrors.NewConflictError("Access ip %s has been used", accessIpAddr) } zoneObj := accessNet.GetZone() if zoneObj == nil { - return nil, httperrors.NewInputParameterError("Access network has no zone???") + return input, httperrors.NewInputParameterError("Access network has no zone???") } - originZoneId, _ := data.GetString("zone_id") + originZoneId := input.Zone // data.GetString("zone_id") if len(originZoneId) > 0 && originZoneId != zoneObj.GetId() { - return nil, httperrors.NewInputParameterError("Access address located in different zone than specified") + return input, httperrors.NewInputParameterError("Access address located in different zone than specified") } // check ip has been reserved @@ -2921,90 +2963,93 @@ func (manager *SHostManager) ValidateCreateData(ctx context.Context, userCred mc // if not reserved, reserve this IP temporarily err = accessNet.reserveIpWithDuration(ctx, userCred, accessIp, "reserve for baremetal access IP", 30*time.Minute) if err != nil { - return nil, err + return input, err } } - data.Set("access_ip", jsonutils.NewString(accessIp)) - data.Set("zone_id", jsonutils.NewString(zoneObj.GetId())) + + input.AccessIp = accessIp + input.Zone = zoneObj.GetId() + // data.Set("access_ip", jsonutils.NewString(accessIp)) + // data.Set("zone_id", jsonutils.NewString(zoneObj.GetId())) } } // only baremetal can be created - hostType, _ := data.GetString("host_type") + hostType := input.HostType // .GetString("host_type") if len(hostType) == 0 { hostType = api.HOST_TYPE_BAREMETAL - data.Set("host_type", jsonutils.NewString(hostType)) + input.HostType = hostType + // data.Set("host_type", jsonutils.NewString(hostType)) } if hostType == api.HOST_TYPE_BAREMETAL { - data.Set("is_baremetal", jsonutils.JSONTrue) + isBaremetal := true + input.IsBaremetal = &isBaremetal + // data.Set("is_baremetal", jsonutils.JSONTrue) } if noProbe { - accessMac, _ := data.GetString("access_mac") - uuid, _ := data.GetString("uuid") - if len(accessMac) == 0 && len(uuid) == 0 { - return nil, httperrors.NewInputParameterError("missing access_mac and uuid in no_probe mode") + // accessMac := input.AccessMac // data.GetString("access_mac") + // uuid := input.Uuid // data.GetString("uuid") + if len(input.AccessMac) == 0 && len(input.Uuid) == 0 { + return input, httperrors.NewInputParameterError("missing access_mac and uuid in no_probe mode") } } - input := apis.EnabledStatusStandaloneResourceCreateInput{} - err = data.Unmarshal(&input) + input.EnabledStatusInfrasResourceBaseCreateInput, err = manager.SEnabledStatusInfrasResourceBaseManager.ValidateCreateData(ctx, userCred, ownerId, query, input.EnabledStatusInfrasResourceBaseCreateInput) if err != nil { - return nil, httperrors.NewInternalServerError("unmarshal EnabledStatusStandaloneCreateInput fail %s", err) + return input, errors.Wrap(err, "SEnabledStatusInfrasResourceBaseManager.ValidateCreateData") } - input, err = manager.SEnabledStatusStandaloneResourceBaseManager.ValidateCreateData(ctx, userCred, ownerId, query, input) - if err != nil { - return nil, err - } - data.Update(jsonutils.Marshal(input)) - return data, nil + return input, nil } -func (self *SHost) ValidateUpdateData(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, data *jsonutils.JSONDict) (*jsonutils.JSONDict, error) { - data, err := HostManager.inputUniquenessCheck(data, self.ZoneId, self.Id) +func (self *SHost) ValidateUpdateData(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, input api.HostUpdateInput) (api.HostUpdateInput, error) { + var err error + input.HostAccessAttributes, err = HostManager.inputUniquenessCheck(input.HostAccessAttributes, self.ZoneId, self.Id) if err != nil { - return nil, err + return input, errors.Wrap(err, "inputUniquenessCheck") } - data, err = HostManager.ValidateSizeParams(data) + input.HostSizeAttributes, err = HostManager.ValidateSizeParams(input.HostSizeAttributes) if err != nil { - return nil, httperrors.NewInputParameterError(err.Error()) + return input, errors.Wrap(err, "ValidateSizeParams") } - ipmiInfo, err := fetchIpmiInfo(data, self.Id) + + ipmiInfo, err := fetchIpmiInfo(input.HostIpmiAttributes, self.Id) if err != nil { - return nil, err + return input, errors.Wrap(err, "fetchIpmiInfo") } - if ipmiInfo.Length() > 0 { - ipmiIpAddr, _ := ipmiInfo.GetString("ip_addr") + ipmiInfoJson := jsonutils.Marshal(ipmiInfo).(*jsonutils.JSONDict) + if ipmiInfoJson.Length() > 0 { + ipmiIpAddr := ipmiInfo.IpAddr if len(ipmiIpAddr) > 0 { net, _ := NetworkManager.GetOnPremiseNetworkOfIP(ipmiIpAddr, "", tristate.None) if net == nil { - return nil, httperrors.NewInputParameterError("%s is out of network IP ranges", ipmiIpAddr) + return input, httperrors.NewInputParameterError("%s is out of network IP ranges", ipmiIpAddr) } zoneObj := net.GetZone() if zoneObj == nil { - return nil, httperrors.NewInputParameterError("IPMI network has not zone???") + return input, httperrors.NewInputParameterError("IPMI network has not zone???") } if zoneObj.GetId() != self.ZoneId { - return nil, httperrors.NewInputParameterError("New IPMI address located in another zone!") + return input, httperrors.NewInputParameterError("New IPMI address located in another zone!") } } val := jsonutils.NewDict() val.Update(self.IpmiInfo) - val.Update(ipmiInfo) - data.Set("ipmi_info", val) + val.Update(ipmiInfoJson) + input.IpmiInfo = val } - data, err = self.SEnabledStatusStandaloneResourceBase.ValidateUpdateData(ctx, userCred, query, data) + input.EnabledStatusInfrasResourceBaseUpdateInput, err = self.SEnabledStatusInfrasResourceBase.ValidateUpdateData(ctx, userCred, query, input.EnabledStatusInfrasResourceBaseUpdateInput) if err != nil { - return nil, err + return input, errors.Wrap(err, "SEnabledStatusInfrasResourceBase.ValidateUpdateData") } - if data.Contains("name") { + if len(input.Name) > 0 { self.UpdateDnsRecords(false) } - return data, nil + return input, nil } func (self *SHost) PostUpdate(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, data jsonutils.JSONObject) { - self.SEnabledStatusStandaloneResourceBase.PostUpdate(ctx, userCred, query, data) + self.SEnabledStatusInfrasResourceBase.PostUpdate(ctx, userCred, query, data) if data.Contains("cpu_cmtbound") || data.Contains("mem_cmtbound") { self.ClearSchedDescCache() @@ -3043,33 +3088,44 @@ func (self *SHost) GetNetifName(netif *SNetInterface) string { return "" } -func fetchIpmiInfo(data *jsonutils.JSONDict, hostId string) (*jsonutils.JSONDict, error) { - IPMI_KEY_PERFIX := "ipmi_" - ipmiInfo := jsonutils.NewDict() - kv, _ := data.GetMap() - var err error - for key := range kv { - if strings.HasPrefix(key, IPMI_KEY_PERFIX) { - value, _ := data.GetString(key) - subkey := key[len(IPMI_KEY_PERFIX):] - if subkey == "password" && len(hostId) > 0 { - value, err = utils.EncryptAESBase64(hostId, value) - if err != nil { - log.Errorf("encrypt password failed %s", err) - return nil, err - } - } else if subkey == "ip_addr" { - if !regutils.MatchIP4Addr(value) { - msg := fmt.Sprintf("%s: %s not valid ipv4 address", key, value) - log.Errorf(msg) - err = fmt.Errorf(msg) - return nil, err - } - } - ipmiInfo.Set(subkey, jsonutils.NewString(value)) +func fetchIpmiInfo(data api.HostIpmiAttributes, hostId string) (types.SIPMIInfo, error) { + info := types.SIPMIInfo{} + info.Username = data.IpmiUsername + if len(hostId) > 0 { + value, err := utils.EncryptAESBase64(hostId, data.IpmiPassword) + if err != nil { + log.Errorf("encrypt password failed %s", err) + return info, errors.Wrap(err, "utils.EncryptAESBase64") } + info.Password = value + } else { + info.Password = data.IpmiPassword } - return ipmiInfo, nil + if len(data.IpmiIpAddr) > 0 && !regutils.MatchIP4Addr(data.IpmiIpAddr) { + msg := fmt.Sprintf("ipmi_ip_addr: %s not valid ipv4 address", data.IpmiIpAddr) + log.Errorf(msg) + return info, errors.Wrap(httperrors.ErrInvalidFormat, msg) + } + info.IpAddr = data.IpmiIpAddr + if data.IpmiPresent != nil { + info.Present = *data.IpmiPresent + } + if data.IpmiLanChannel != nil { + info.LanChannel = *data.IpmiLanChannel + } + if data.IpmiVerified != nil { + info.Verified = *data.IpmiVerified + } + if data.IpmiRedfishApi != nil { + info.RedfishApi = *data.IpmiRedfishApi + } + if data.IpmiCdromBoot != nil { + info.CdromBoot = *data.IpmiCdromBoot + } + if data.IpmiPxeBoot != nil { + info.PxeBoot = *data.IpmiPxeBoot + } + return info, nil } func (self *SHost) AllowPerformStart(ctx context.Context, @@ -3986,7 +4042,7 @@ func (self *SHost) AllowPerformEnable( query jsonutils.JSONObject, input apis.PerformEnableInput, ) bool { - return self.SEnabledStatusStandaloneResourceBase.AllowPerformEnable(ctx, userCred, query, input) + return self.SEnabledStatusInfrasResourceBase.AllowPerformEnable(ctx, userCred, query, input) } func (self *SHost) PerformEnable( @@ -3996,9 +4052,9 @@ func (self *SHost) PerformEnable( input apis.PerformEnableInput, ) (jsonutils.JSONObject, error) { if !self.GetEnabled() { - _, err := self.SEnabledStatusStandaloneResourceBase.PerformEnable(ctx, userCred, query, input) + _, err := self.SEnabledStatusInfrasResourceBase.PerformEnable(ctx, userCred, query, input) if err != nil { - return nil, errors.Wrap(err, "SEnabledStatusStandaloneResourceBase.PerformEnable") + return nil, errors.Wrap(err, "SEnabledStatusInfrasResourceBase.PerformEnable") } self.SyncAttachedStorageStatus() } @@ -4011,14 +4067,14 @@ func (self *SHost) AllowPerformDisable( query jsonutils.JSONObject, input apis.PerformDisableInput, ) bool { - return self.SEnabledStatusStandaloneResourceBase.AllowPerformDisable(ctx, userCred, query, input) + return self.SEnabledStatusInfrasResourceBase.AllowPerformDisable(ctx, userCred, query, input) } func (self *SHost) PerformDisable(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, input apis.PerformDisableInput) (jsonutils.JSONObject, error) { if self.GetEnabled() { - _, err := self.SEnabledStatusStandaloneResourceBase.PerformDisable(ctx, userCred, query, input) + _, err := self.SEnabledStatusInfrasResourceBase.PerformDisable(ctx, userCred, query, input) if err != nil { - return nil, errors.Wrap(err, "SEnabledStatusStandaloneResourceBase.PerformDisable") + return nil, errors.Wrap(err, "SEnabledStatusInfrasResourceBase.PerformDisable") } self.SyncAttachedStorageStatus() } @@ -4449,7 +4505,7 @@ func (self *SHost) getCloudProviderInfo() SCloudProviderInfo { } func (self *SHost) GetShortDesc(ctx context.Context) *jsonutils.JSONDict { - desc := self.SEnabledStatusStandaloneResourceBase.GetShortDesc(ctx) + desc := self.SEnabledStatusInfrasResourceBase.GetShortDesc(ctx) info := self.getCloudProviderInfo() desc.Update(jsonutils.Marshal(&info)) return desc @@ -4505,9 +4561,9 @@ func (host *SHost) GetDynamicConditionInput() *jsonutils.JSONDict { } func (host *SHost) PerformStatus(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, input apis.PerformStatusInput) (jsonutils.JSONObject, error) { - ret, err := host.SEnabledStatusStandaloneResourceBase.PerformStatus(ctx, userCred, query, input) + ret, err := host.SEnabledStatusInfrasResourceBase.PerformStatus(ctx, userCred, query, input) if err != nil { - return nil, errors.Wrap(err, "SEnabledStatusStandaloneResourceBase.PerformStatus") + return nil, errors.Wrap(err, "SEnabledStatusInfrasResourceBase.PerformStatus") } host.ClearSchedDescCache() return ret, nil @@ -4598,7 +4654,7 @@ func (host *SHost) PerformHostMaintenance(ctx context.Context, userCred mcclient } func (host *SHost) SetStatus(userCred mcclient.TokenCredential, status string, reason string) error { - err := host.SEnabledStatusStandaloneResourceBase.SetStatus(userCred, status, reason) + err := host.SEnabledStatusInfrasResourceBase.SetStatus(userCred, status, reason) if err != nil { return err } @@ -4850,3 +4906,22 @@ func (self *SHost) StartSyncConfig(ctx context.Context, userCred mcclient.TokenC task.ScheduleRun(nil) return nil } + +func (model *SHost) CustomizeCreate(ctx context.Context, userCred mcclient.TokenCredential, ownerId mcclient.IIdentityProvider, query jsonutils.JSONObject, data jsonutils.JSONObject) error { + // make host default public + model.IsPublic = true + model.PublicScope = string(rbacutils.ScopeSystem) + return model.SEnabledStatusInfrasResourceBase.CustomizeCreate(ctx, userCred, ownerId, query, data) +} + +func (host *SHost) PerformChangeOwner(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, input apis.PerformChangeDomainOwnerInput) (jsonutils.JSONObject, error) { + localStorages := host.GetAttachedLocalStorages() + for i := range localStorages { + _, err := localStorages[i].PerformChangeOwner(ctx, userCred, query, input) + if err != nil { + return nil, errors.Wrap(err, "local storage change owner") + } + } + + return host.SEnabledStatusInfrasResourceBase.PerformChangeOwner(ctx, userCred, query, input) +} diff --git a/pkg/compute/models/loadbalanceracls.go b/pkg/compute/models/loadbalanceracls.go index 0a12401b0c..e99d6412bc 100644 --- a/pkg/compute/models/loadbalanceracls.go +++ b/pkg/compute/models/loadbalanceracls.go @@ -224,7 +224,17 @@ func (lbacl *SLoadbalancerAcl) ValidateUpdateData(ctx context.Context, userCred if err != nil { return nil, err } - return lbacl.SSharableVirtualResourceBase.ValidateUpdateData(ctx, userCred, query, data) + input := apis.SharableVirtualResourceBaseUpdateInput{} + err = data.Unmarshal(&input) + if err != nil { + return nil, errors.Wrap(err, "Unmarshal") + } + input, err = lbacl.SSharableVirtualResourceBase.ValidateUpdateData(ctx, userCred, query, input) + if err != nil { + return nil, errors.Wrap(err, "SSharableVirtualResourceBase.ValidateUpdateData") + } + data.Update(jsonutils.Marshal(input)) + return data, nil } func (lbacl *SLoadbalancerAcl) PostUpdate(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, data jsonutils.JSONObject) { diff --git a/pkg/compute/models/loadbalancerbackends.go b/pkg/compute/models/loadbalancerbackends.go index 43b353ac40..cdcb0fab4a 100644 --- a/pkg/compute/models/loadbalancerbackends.go +++ b/pkg/compute/models/loadbalancerbackends.go @@ -301,10 +301,18 @@ func (man *SLoadbalancerBackendManager) GetGuestAddress(guest *SGuest) (string, } func (lbb *SLoadbalancerBackend) ValidateUpdateData(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, data *jsonutils.JSONDict) (*jsonutils.JSONDict, error) { - _, err := lbb.SVirtualResourceBase.ValidateUpdateData(ctx, userCred, query, data) + var err error + input := apis.VirtualResourceBaseUpdateInput{} + err = data.Unmarshal(&input) if err != nil { - return nil, err + return nil, errors.Wrap(err, "Unmarshal") } + input, err = lbb.SVirtualResourceBase.ValidateUpdateData(ctx, userCred, query, input) + if err != nil { + return nil, errors.Wrap(err, "SVirtualResourceBase.ValidateUpdateData") + } + data.Update(jsonutils.Marshal(input)) + region := lbb.GetRegion() if region == nil { return nil, httperrors.NewResourceNotFoundError("failed to found region for loadbalancer backend %s", lbb.Name) diff --git a/pkg/compute/models/loadbalancercachedacls.go b/pkg/compute/models/loadbalancercachedacls.go index 786be325d2..838a3a0558 100644 --- a/pkg/compute/models/loadbalancercachedacls.go +++ b/pkg/compute/models/loadbalancercachedacls.go @@ -77,7 +77,17 @@ func (lbacl *SCachedLoadbalancerAcl) ValidateUpdateData(ctx context.Context, use if err != nil { return nil, err } - return lbacl.SSharableVirtualResourceBase.ValidateUpdateData(ctx, userCred, query, data) + input := apis.SharableVirtualResourceBaseUpdateInput{} + err = data.Unmarshal(&input) + if err != nil { + return nil, errors.Wrap(err, "Unmarshal") + } + input, err = lbacl.SSharableVirtualResourceBase.ValidateUpdateData(ctx, userCred, query, input) + if err != nil { + return nil, errors.Wrap(err, "SSharableVirtualResourceBase.ValidateUpdateData") + } + data.Update(jsonutils.Marshal(input)) + return data, nil } func (lbacl *SCachedLoadbalancerAcl) PostUpdate(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, data jsonutils.JSONObject) { diff --git a/pkg/compute/models/loadbalancercertificates.go b/pkg/compute/models/loadbalancercertificates.go index 1ff5d1eee4..03bb490ac6 100644 --- a/pkg/compute/models/loadbalancercertificates.go +++ b/pkg/compute/models/loadbalancercertificates.go @@ -112,9 +112,16 @@ func (lbcert *SLoadbalancerCertificate) ValidateUpdateData(ctx context.Context, updateData.Set("description", jsonutils.NewString(desc)) } - if _, err := lbcert.SVirtualResourceBase.ValidateUpdateData(ctx, userCred, query, updateData); err != nil { - return nil, err + input := apis.VirtualResourceBaseUpdateInput{} + err := updateData.Unmarshal(&input) + if err != nil { + return nil, errors.Wrap(err, "Unmarshal") } + input, err = lbcert.SVirtualResourceBase.ValidateUpdateData(ctx, userCred, query, input) + if err != nil { + return nil, errors.Wrap(err, "SVirtualResourceBase.ValidateUpdateData") + } + updateData.Update(jsonutils.Marshal(input)) return updateData, nil } diff --git a/pkg/compute/models/loadbalancerclusters.go b/pkg/compute/models/loadbalancerclusters.go index 8cf7ed25df..f97ef0e8ed 100644 --- a/pkg/compute/models/loadbalancerclusters.go +++ b/pkg/compute/models/loadbalancerclusters.go @@ -187,7 +187,19 @@ func (lbc *SLoadbalancerCluster) ValidateUpdateData(ctx context.Context, userCre log.Infof("changing wire attribute of lbcluster %s(%s) %sto %s(%s)", lbc.Name, lbc.Id, from, wire.Name, wire.Id) } - return lbc.SStandaloneResourceBase.ValidateUpdateData(ctx, userCred, query, data) + + input := apis.StandaloneResourceBaseUpdateInput{} + err := data.Unmarshal(&input) + if err != nil { + return nil, errors.Wrap(err, "Unmarshal") + } + input, err = lbc.SStandaloneResourceBase.ValidateUpdateData(ctx, userCred, query, input) + if err != nil { + return nil, errors.Wrap(err, "SStandaloneResourceBase.ValidateUpdateData") + } + data.Update(jsonutils.Marshal(input)) + + return data, nil } func (lbc *SLoadbalancerCluster) ValidateDeleteCondition(ctx context.Context) error { diff --git a/pkg/compute/models/loadbalancerlistenerrules.go b/pkg/compute/models/loadbalancerlistenerrules.go index f060cc2317..bed4b879fb 100644 --- a/pkg/compute/models/loadbalancerlistenerrules.go +++ b/pkg/compute/models/loadbalancerlistenerrules.go @@ -555,9 +555,16 @@ func (lbr *SLoadbalancerListenerRule) ValidateUpdateData(ctx context.Context, us return nil, err } - if _, err := lbr.SVirtualResourceBase.ValidateUpdateData(ctx, userCred, query, data); err != nil { - return nil, err + input := apis.VirtualResourceBaseUpdateInput{} + err := data.Unmarshal(&input) + if err != nil { + return nil, errors.Wrap(err, "Unmarshal") } + input, err = lbr.SVirtualResourceBase.ValidateUpdateData(ctx, userCred, query, input) + if err != nil { + return nil, errors.Wrap(err, "SVirtualResourceBase.ValidateUpdateData") + } + data.Update(jsonutils.Marshal(input)) region := lbr.GetRegion() if region == nil { diff --git a/pkg/compute/models/loadbalancerlisteners.go b/pkg/compute/models/loadbalancerlisteners.go index a70306d971..dbce43900b 100644 --- a/pkg/compute/models/loadbalancerlisteners.go +++ b/pkg/compute/models/loadbalancerlisteners.go @@ -422,9 +422,16 @@ func (lblis *SLoadbalancerListener) ValidateUpdateData(ctx context.Context, user return nil, err } - if _, err := lblis.SVirtualResourceBase.ValidateUpdateData(ctx, userCred, query, data); err != nil { - return nil, err + input := apis.VirtualResourceBaseUpdateInput{} + err := data.Unmarshal(&input) + if err != nil { + return nil, errors.Wrap(err, "Unmarshal") } + input, err = lblis.SVirtualResourceBase.ValidateUpdateData(ctx, userCred, query, input) + if err != nil { + return nil, errors.Wrap(err, "SVirtualResourceBase.ValidateUpdateData") + } + data.Update(jsonutils.Marshal(input)) region := lblis.GetRegion() if region == nil { diff --git a/pkg/compute/models/loadbalancers.go b/pkg/compute/models/loadbalancers.go index ab9375c1ca..fc0b2d0101 100644 --- a/pkg/compute/models/loadbalancers.go +++ b/pkg/compute/models/loadbalancers.go @@ -476,7 +476,18 @@ func (lb *SLoadbalancer) ValidateUpdateData(ctx context.Context, userCred mcclie return nil, httperrors.NewInputParameterError("backend group %s(%s) belongs to loadbalancer %s, not %s", backendGroup.Name, backendGroup.Id, backendGroup.LoadbalancerId, lb.Id) } - return lb.SVirtualResourceBase.ValidateUpdateData(ctx, userCred, query, data) + input := apis.VirtualResourceBaseUpdateInput{} + err = data.Unmarshal(&input) + if err != nil { + return nil, errors.Wrap(err, "Unmarshal") + } + input, err = lb.SVirtualResourceBase.ValidateUpdateData(ctx, userCred, query, input) + if err != nil { + return nil, errors.Wrap(err, "SVirtualResourceBase.ValidateUpdateData") + } + data.Update(jsonutils.Marshal(input)) + + return data, nil } func (man *SLoadbalancerManager) FetchCustomizeColumns( diff --git a/pkg/compute/models/managedresource.go b/pkg/compute/models/managedresource.go index baafa626f8..e1b778de8c 100644 --- a/pkg/compute/models/managedresource.go +++ b/pkg/compute/models/managedresource.go @@ -17,9 +17,12 @@ package models import ( "context" "database/sql" - "fmt" "strings" + "yunion.io/x/pkg/utils" + + "yunion.io/x/onecloud/pkg/util/rbacutils" + "yunion.io/x/jsonutils" "yunion.io/x/log" "yunion.io/x/pkg/errors" @@ -75,7 +78,7 @@ func (self *SManagedResourceBase) GetRegionDriver() (IRegionDriver, error) { } driver := GetRegionDriver(provider) if driver == nil { - return nil, fmt.Errorf("failed to get %s region drivder", provider) + return nil, errors.Wrapf(httperrors.ErrInvalidStatus, "failed to get %s region drivder", provider) } return driver, nil } @@ -86,7 +89,7 @@ func (self *SManagedResourceBase) GetProviderFactory() (cloudprovider.ICloudProv if len(self.ManagerId) > 0 { return nil, cloudprovider.ErrInvalidProvider } - return nil, fmt.Errorf("Resource is self managed") + return nil, errors.Wrap(httperrors.ErrInvalidStatus, "Resource is self managed") } return provider.GetProviderFactory() } @@ -97,7 +100,7 @@ func (self *SManagedResourceBase) GetDriver() (cloudprovider.ICloudProvider, err if len(self.ManagerId) > 0 { return nil, cloudprovider.ErrInvalidProvider } - return nil, fmt.Errorf("Resource is self managed") + return nil, errors.Wrap(httperrors.ErrInvalidStatus, "Resource is self managed") } return provider.GetProvider() } @@ -122,6 +125,47 @@ func (self *SManagedResourceBase) IsManaged() bool { return len(self.ManagerId) > 0 } +func (self *SManagedResourceBase) CanShareToDomain(domainId string) bool { + provider := self.GetCloudprovider() + if provider == nil { + return true + } + account := provider.GetCloudaccount() + if account == nil { + // no cloud account, can share to any domain + return true + } + switch account.ShareMode { + case api.CLOUD_ACCOUNT_SHARE_MODE_ACCOUNT_DOMAIN: + if domainId == account.DomainId { + return true + } else { + return false + } + case api.CLOUD_ACCOUNT_SHARE_MODE_PROVIDER_DOMAIN: + if domainId == provider.DomainId { + return true + } else { + return false + } + case api.CLOUD_ACCOUNT_SHARE_MODE_SYSTEM: + if account.PublicScope == string(rbacutils.ScopeSystem) { + return true + } else { + // public_scope = domain + if domainId == account.DomainId { + return true + } + if utils.IsInStringArray(domainId, account.GetSharedDomains()) { + return true + } + return false + } + default: + return true + } +} + func (self *SManagedResourceBase) GetExtraDetails(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, isList bool) api.ManagedResourceInfo { return api.ManagedResourceInfo{} } @@ -303,9 +347,10 @@ func _managedResourceFilterByDomain(managerIdFieldName string, q *sqlchemy.SQuer } return nil, httperrors.NewGeneralError(err) } - accounts := CloudaccountManager.Query().SubQuery() - providers := CloudproviderManager.Query().SubQuery() - subq := providers.Query(providers.Field("id")) + accounts := CloudaccountManager.Query("id") + accounts = CloudaccountManager.filterByDomainId(accounts, domain.GetId()) + subq := CloudproviderManager.Query("id").In("cloudaccount_id", accounts.SubQuery()) + /*subq := providers.Query(providers.Field("id")) subq = subq.Join(accounts, sqlchemy.Equals(providers.Field("cloudaccount_id"), accounts.Field("id"))) subq = subq.Filter(sqlchemy.OR( sqlchemy.AND( @@ -317,7 +362,7 @@ func _managedResourceFilterByDomain(managerIdFieldName string, q *sqlchemy.SQuer sqlchemy.Equals(accounts.Field("domain_id"), domain.GetId()), sqlchemy.Equals(accounts.Field("share_mode"), api.CLOUD_ACCOUNT_SHARE_MODE_ACCOUNT_DOMAIN), ), - )) + ))*/ if len(filterField) == 0 { q = q.Filter(sqlchemy.OR( sqlchemy.IsNullOrEmpty(q.Field(managerIdFieldName)), @@ -454,13 +499,9 @@ func managedResourceFilterByZone(q *sqlchemy.SQuery, query api.ZonalFilterListIn q = q.Filter(sqlchemy.In(q.Field(filterField), sq.SubQuery())) } } else if len(query.Zone) > 0 { - zoneObj, err := ZoneManager.FetchByIdOrName(nil, query.Zone) + zoneObj, err := ValidateZoneResourceInput(nil, query.ZoneResourceInput) if err != nil { - if err == sql.ErrNoRows { - return nil, httperrors.NewResourceNotFoundError2(ZoneManager.Keyword(), query.Zone) - } else { - return nil, httperrors.NewGeneralError(err) - } + return nil, errors.Wrap(err, "ValidateZoneResourceInput") } if len(filterField) == 0 { q = q.Filter(sqlchemy.Equals(q.Field("zone_id"), zoneObj.GetId())) diff --git a/pkg/compute/models/natdtable.go b/pkg/compute/models/natdtable.go index 482f0327e6..4a159537ce 100644 --- a/pkg/compute/models/natdtable.go +++ b/pkg/compute/models/natdtable.go @@ -184,7 +184,9 @@ func (manager *SNatDEntryManager) checkIPPort(input *api.SNatDCreateInput) (*SEl return eip, nil } -func (manager *SNatDEntryManager) SyncNatDTable(ctx context.Context, userCred mcclient.TokenCredential, syncOwnerId mcclient.IIdentityProvider, provider *SCloudprovider, nat *SNatGateway, extDTable []cloudprovider.ICloudNatDEntry) compare.SyncResult { +func (manager *SNatDEntryManager) SyncNatDTable(ctx context.Context, userCred mcclient.TokenCredential, provider *SCloudprovider, nat *SNatGateway, extDTable []cloudprovider.ICloudNatDEntry) compare.SyncResult { + syncOwnerId := provider.GetOwnerId() + lockman.LockClass(ctx, manager, db.GetLockClassKey(manager, syncOwnerId)) defer lockman.ReleaseClass(ctx, manager, db.GetLockClassKey(manager, syncOwnerId)) @@ -214,7 +216,7 @@ func (manager *SNatDEntryManager) SyncNatDTable(ctx context.Context, userCred mc } for i := 0; i < len(commondb); i += 1 { - err := commondb[i].SyncWithCloudNatDTable(ctx, userCred, commonext[i]) + err := commondb[i].SyncWithCloudNatDTable(ctx, userCred, commonext[i], syncOwnerId) if err != nil { result.UpdateError(err) continue @@ -246,7 +248,7 @@ func (self *SNatDEntry) syncRemoveCloudNatDTable(ctx context.Context, userCred m return self.RealDelete(ctx, userCred) } -func (self *SNatDEntry) SyncWithCloudNatDTable(ctx context.Context, userCred mcclient.TokenCredential, extEntry cloudprovider.ICloudNatDEntry) error { +func (self *SNatDEntry) SyncWithCloudNatDTable(ctx context.Context, userCred mcclient.TokenCredential, extEntry cloudprovider.ICloudNatDEntry, syncOwnerId mcclient.IIdentityProvider) error { diff, err := db.UpdateWithLock(ctx, self, func() error { self.Status = extEntry.GetStatus() self.ExternalIP = extEntry.GetExternalIp() @@ -259,6 +261,9 @@ func (self *SNatDEntry) SyncWithCloudNatDTable(ctx context.Context, userCred mcc if err != nil { return err } + + SyncCloudDomain(userCred, self, syncOwnerId) + db.OpsLog.LogSyncUpdate(self, diff, userCred) return nil } @@ -284,6 +289,8 @@ func (manager *SNatDEntryManager) newFromCloudNatDTable(ctx context.Context, use return nil, err } + SyncCloudDomain(userCred, &table, ownerId) + db.OpsLog.LogEvent(&table, db.ACT_CREATE, table.GetShortDesc(ctx), userCred) return &table, nil diff --git a/pkg/compute/models/natgateways.go b/pkg/compute/models/natgateways.go index b54dec6457..62910e68cb 100644 --- a/pkg/compute/models/natgateways.go +++ b/pkg/compute/models/natgateways.go @@ -38,7 +38,7 @@ import ( ) type SNatGatewayManager struct { - db.SStatusStandaloneResourceBaseManager + db.SStatusInfrasResourceBaseManager db.SExternalizedResourceBaseManager SVpcResourceBaseManager // SManagedResourceBaseManager @@ -48,7 +48,7 @@ var NatGatewayManager *SNatGatewayManager func init() { NatGatewayManager = &SNatGatewayManager{ - SStatusStandaloneResourceBaseManager: db.NewStatusStandaloneResourceBaseManager( + SStatusInfrasResourceBaseManager: db.NewStatusInfrasResourceBaseManager( SNatGateway{}, "natgateways_tbl", "natgateway", @@ -59,7 +59,7 @@ func init() { } type SNatGateway struct { - db.SStatusStandaloneResourceBase + db.SStatusInfrasResourceBase db.SExternalizedResourceBase // SManagedResourceBase SBillingResourceBase @@ -81,9 +81,9 @@ func (man *SNatGatewayManager) ListItemFilter( userCred mcclient.TokenCredential, query api.NatGetewayListInput, ) (*sqlchemy.SQuery, error) { - q, err := man.SStatusStandaloneResourceBaseManager.ListItemFilter(ctx, q, userCred, query.StatusStandaloneResourceListInput) + q, err := man.SStatusInfrasResourceBaseManager.ListItemFilter(ctx, q, userCred, query.StatusInfrasResourceBaseListInput) if err != nil { - return nil, errors.Wrap(err, "SStatusDomainLevelResourceBaseManager.ListItemFilter") + return nil, errors.Wrap(err, "SStatusInfrasResourceBaseManager.ListItemFilter") } q, err = man.SExternalizedResourceBaseManager.ListItemFilter(ctx, q, userCred, query.ExternalizedResourceBaseListInput) if err != nil { @@ -103,9 +103,9 @@ func (man *SNatGatewayManager) OrderByExtraFields( userCred mcclient.TokenCredential, query api.NatGetewayListInput, ) (*sqlchemy.SQuery, error) { - q, err := man.SStatusStandaloneResourceBaseManager.OrderByExtraFields(ctx, q, userCred, query.StatusStandaloneResourceListInput) + q, err := man.SStatusInfrasResourceBaseManager.OrderByExtraFields(ctx, q, userCred, query.StatusInfrasResourceBaseListInput) if err != nil { - return nil, errors.Wrap(err, "SStatusDomainLevelResourceBaseManager.OrderByExtraFields") + return nil, errors.Wrap(err, "SStatusInfrasResourceBaseManager.OrderByExtraFields") } q, err = man.SVpcResourceBaseManager.OrderByExtraFields(ctx, q, userCred, query.VpcFilterListInput) if err != nil { @@ -266,11 +266,11 @@ func (manager SNatGatewayManager) FetchCustomizeColumns( isList bool, ) []api.NatgatewayDetails { rows := make([]api.NatgatewayDetails, len(objs)) - stdRows := manager.SStatusStandaloneResourceBaseManager.FetchCustomizeColumns(ctx, userCred, query, objs, fields, isList) + stdRows := manager.SStatusInfrasResourceBaseManager.FetchCustomizeColumns(ctx, userCred, query, objs, fields, isList) vpcRows := manager.SVpcResourceBaseManager.FetchCustomizeColumns(ctx, userCred, query, objs, fields, isList) for i := range rows { rows[i] = api.NatgatewayDetails{ - StatusStandaloneResourceDetails: stdRows[i], + StatusInfrasResourceBaseDetails: stdRows[i], VpcResourceInfo: vpcRows[i], } rows[i], _ = objs[i].(*SNatGateway).getMoreDetails(ctx, userCred, rows[i]) @@ -371,6 +371,9 @@ func (self *SNatGateway) SyncWithCloudNatGateway(ctx context.Context, userCred m if err != nil { return err } + + SyncCloudDomain(userCred, self, provider.GetOwnerId()) + db.OpsLog.LogSyncUpdate(self, diff, userCred) return nil } @@ -412,6 +415,8 @@ func (manager *SNatGatewayManager) newFromCloudNatGateway(ctx context.Context, u return nil, errors.Wrap(err, "Insert") } + SyncCloudDomain(userCred, &nat, provider.GetOwnerId()) + db.OpsLog.LogEvent(&nat, db.ACT_CREATE, nat.GetShortDesc(ctx), userCred) return &nat, nil @@ -561,19 +566,19 @@ type INatHelper interface { } type SNatEntryManager struct { - db.SStatusStandaloneResourceBaseManager + db.SStatusInfrasResourceBaseManager db.SExternalizedResourceBaseManager SNatgatewayResourceBaseManager } func NewNatEntryManager(dt interface{}, tableName string, keyword string, keywordPlural string) SNatEntryManager { return SNatEntryManager{ - SStatusStandaloneResourceBaseManager: db.NewStatusStandaloneResourceBaseManager(dt, tableName, keyword, keywordPlural), + SStatusInfrasResourceBaseManager: db.NewStatusInfrasResourceBaseManager(dt, tableName, keyword, keywordPlural), } } type SNatEntry struct { - db.SStatusStandaloneResourceBase + db.SStatusInfrasResourceBase db.SExternalizedResourceBase SNatgatewayResourceBase `width:"36" charset:"ascii" nullable:"false" list:"user" create:"required"` @@ -593,9 +598,9 @@ func (man *SNatEntryManager) ListItemFilter( userCred mcclient.TokenCredential, query api.NatEntryListInput, ) (*sqlchemy.SQuery, error) { - q, err := man.SStatusStandaloneResourceBaseManager.ListItemFilter(ctx, q, userCred, query.StatusStandaloneResourceListInput) + q, err := man.SStatusInfrasResourceBaseManager.ListItemFilter(ctx, q, userCred, query.StatusInfrasResourceBaseListInput) if err != nil { - return nil, errors.Wrap(err, "SStandaloneResourceBaseManager.ListItemFilter") + return nil, errors.Wrap(err, "SStatusInfrasResourceBaseManager.ListItemFilter") } q, err = man.SExternalizedResourceBaseManager.ListItemFilter(ctx, q, userCred, query.ExternalizedResourceBaseListInput) if err != nil { @@ -623,9 +628,9 @@ func (man *SNatEntryManager) OrderByExtraFields( userCred mcclient.TokenCredential, query api.NatEntryListInput, ) (*sqlchemy.SQuery, error) { - q, err := man.SStatusStandaloneResourceBaseManager.OrderByExtraFields(ctx, q, userCred, query.StatusStandaloneResourceListInput) + q, err := man.SStatusInfrasResourceBaseManager.OrderByExtraFields(ctx, q, userCred, query.StatusInfrasResourceBaseListInput) if err != nil { - return nil, errors.Wrap(err, "SStatusStandaloneResourceBaseManager.OrderByExtraFields") + return nil, errors.Wrap(err, "SStatusloneResourceBaseManager.OrderByExtraFields") } q, err = man.SNatgatewayResourceBaseManager.OrderByExtraFields(ctx, q, userCred, query.NatGatewayFilterListInput) if err != nil { @@ -637,7 +642,7 @@ func (man *SNatEntryManager) OrderByExtraFields( func (man *SNatEntryManager) QueryDistinctExtraField(q *sqlchemy.SQuery, field string) (*sqlchemy.SQuery, error) { var err error - q, err = man.SStatusStandaloneResourceBaseManager.QueryDistinctExtraField(q, field) + q, err = man.SStatusInfrasResourceBaseManager.QueryDistinctExtraField(q, field) if err == nil { return q, nil } @@ -667,11 +672,11 @@ func (manager *SNatEntryManager) FetchCustomizeColumns( isList bool, ) []api.NatEntryDetails { rows := make([]api.NatEntryDetails, len(objs)) - stdRows := manager.SStatusStandaloneResourceBaseManager.FetchCustomizeColumns(ctx, userCred, query, objs, fields, isList) + stdRows := manager.SStatusInfrasResourceBaseManager.FetchCustomizeColumns(ctx, userCred, query, objs, fields, isList) natRows := manager.SNatgatewayResourceBaseManager.FetchCustomizeColumns(ctx, userCred, query, objs, fields, isList) for i := range rows { rows[i] = api.NatEntryDetails{ - StatusStandaloneResourceDetails: stdRows[i], + StatusInfrasResourceBaseDetails: stdRows[i], NatGatewayResourceInfo: natRows[i], } var base *SNatEntry diff --git a/pkg/compute/models/natstable.go b/pkg/compute/models/natstable.go index c5a7183319..26ebaa5d29 100644 --- a/pkg/compute/models/natstable.go +++ b/pkg/compute/models/natstable.go @@ -221,7 +221,9 @@ func (man *SNatSEntryManager) ValidateCreateData(ctx context.Context, userCred m return data, nil } -func (manager *SNatSEntryManager) SyncNatSTable(ctx context.Context, userCred mcclient.TokenCredential, syncOwnerId mcclient.IIdentityProvider, provider *SCloudprovider, nat *SNatGateway, extTable []cloudprovider.ICloudNatSEntry) compare.SyncResult { +func (manager *SNatSEntryManager) SyncNatSTable(ctx context.Context, userCred mcclient.TokenCredential, provider *SCloudprovider, nat *SNatGateway, extTable []cloudprovider.ICloudNatSEntry) compare.SyncResult { + syncOwnerId := provider.GetOwnerId() + lockman.LockClass(ctx, manager, db.GetLockClassKey(manager, syncOwnerId)) defer lockman.ReleaseClass(ctx, manager, db.GetLockClassKey(manager, syncOwnerId)) @@ -251,7 +253,7 @@ func (manager *SNatSEntryManager) SyncNatSTable(ctx context.Context, userCred mc } for i := 0; i < len(commondb); i += 1 { - err := commondb[i].SyncWithCloudNatSTable(ctx, userCred, commonext[i]) + err := commondb[i].SyncWithCloudNatSTable(ctx, userCred, commonext[i], syncOwnerId) if err != nil { result.UpdateError(err) continue @@ -283,7 +285,7 @@ func (self *SNatSEntry) syncRemoveCloudNatSTable(ctx context.Context, userCred m return self.RealDelete(ctx, userCred) } -func (self *SNatSEntry) SyncWithCloudNatSTable(ctx context.Context, userCred mcclient.TokenCredential, extEntry cloudprovider.ICloudNatSEntry) error { +func (self *SNatSEntry) SyncWithCloudNatSTable(ctx context.Context, userCred mcclient.TokenCredential, extEntry cloudprovider.ICloudNatSEntry, syncOwnerId mcclient.IIdentityProvider) error { diff, err := db.UpdateWithLock(ctx, self, func() error { self.Status = extEntry.GetStatus() self.IP = extEntry.GetIP() @@ -300,6 +302,9 @@ func (self *SNatSEntry) SyncWithCloudNatSTable(ctx context.Context, userCred mcc if err != nil { return err } + + SyncCloudDomain(userCred, self, syncOwnerId) + db.OpsLog.LogSyncUpdate(self, diff, userCred) return nil } @@ -330,6 +335,8 @@ func (manager *SNatSEntryManager) newFromCloudNatSTable(ctx context.Context, use return nil, err } + SyncCloudDomain(userCred, &table, ownerId) + db.OpsLog.LogEvent(&table, db.ACT_CREATE, table.GetShortDesc(ctx), userCred) return &table, nil diff --git a/pkg/compute/models/networkinterfaces.go b/pkg/compute/models/networkinterfaces.go index c84add775b..106cf02e07 100644 --- a/pkg/compute/models/networkinterfaces.go +++ b/pkg/compute/models/networkinterfaces.go @@ -33,7 +33,7 @@ import ( ) type SNetworkInterfaceManager struct { - db.SStatusStandaloneResourceBaseManager + db.SStatusInfrasResourceBaseManager db.SExternalizedResourceBaseManager SManagedResourceBaseManager SCloudregionResourceBaseManager @@ -43,7 +43,7 @@ var NetworkInterfaceManager *SNetworkInterfaceManager func init() { NetworkInterfaceManager = &SNetworkInterfaceManager{ - SStatusStandaloneResourceBaseManager: db.NewStatusStandaloneResourceBaseManager( + SStatusInfrasResourceBaseManager: db.NewStatusInfrasResourceBaseManager( SNetworkInterface{}, "networkinterfaces_tbl", "networkinterface", @@ -54,7 +54,7 @@ func init() { } type SNetworkInterface struct { - db.SStatusStandaloneResourceBase + db.SStatusInfrasResourceBase db.SExternalizedResourceBase SManagedResourceBase SCloudregionResourceBase @@ -82,9 +82,9 @@ func (manager *SNetworkInterfaceManager) ListItemFilter( ) (*sqlchemy.SQuery, error) { var err error - q, err = manager.SStatusStandaloneResourceBaseManager.ListItemFilter(ctx, q, userCred, query.StatusStandaloneResourceListInput) + q, err = manager.SStatusInfrasResourceBaseManager.ListItemFilter(ctx, q, userCred, query.StatusInfrasResourceBaseListInput) if err != nil { - return nil, errors.Wrap(err, "SStatusStandaloneResourceBaseManager.ListItemFilter") + return nil, errors.Wrap(err, "SStatusInfrasResourceBaseManager.ListItemFilter") } q, err = manager.SExternalizedResourceBaseManager.ListItemFilter(ctx, q, userCred, query.ExternalizedResourceBaseListInput) if err != nil { @@ -120,9 +120,9 @@ func (manager *SNetworkInterfaceManager) OrderByExtraFields( ) (*sqlchemy.SQuery, error) { var err error - q, err = manager.SStatusStandaloneResourceBaseManager.OrderByExtraFields(ctx, q, userCred, query.StatusStandaloneResourceListInput) + q, err = manager.SStatusInfrasResourceBaseManager.OrderByExtraFields(ctx, q, userCred, query.StatusInfrasResourceBaseListInput) if err != nil { - return nil, errors.Wrap(err, "SStatusStandaloneResourceBaseManager.OrderByExtraFields") + return nil, errors.Wrap(err, "SStatusInfrasResourceBaseManager.OrderByExtraFields") } q, err = manager.SManagedResourceBaseManager.OrderByExtraFields(ctx, q, userCred, query.ManagedResourceListInput) if err != nil { @@ -139,7 +139,7 @@ func (manager *SNetworkInterfaceManager) OrderByExtraFields( func (manager *SNetworkInterfaceManager) QueryDistinctExtraField(q *sqlchemy.SQuery, field string) (*sqlchemy.SQuery, error) { var err error - q, err = manager.SStatusStandaloneResourceBaseManager.QueryDistinctExtraField(q, field) + q, err = manager.SStatusInfrasResourceBaseManager.QueryDistinctExtraField(q, field) if err == nil { return q, nil } @@ -197,13 +197,13 @@ func (manager *SNetworkInterfaceManager) FetchCustomizeColumns( ) []api.NetworkInterfaceDetails { rows := make([]api.NetworkInterfaceDetails, len(objs)) - stdRows := manager.SStatusStandaloneResourceBaseManager.FetchCustomizeColumns(ctx, userCred, query, objs, fields, isList) + stdRows := manager.SStatusInfrasResourceBaseManager.FetchCustomizeColumns(ctx, userCred, query, objs, fields, isList) manRows := manager.SManagedResourceBaseManager.FetchCustomizeColumns(ctx, userCred, query, objs, fields, isList) regRows := manager.SCloudregionResourceBaseManager.FetchCustomizeColumns(ctx, userCred, query, objs, fields, isList) for i := range rows { rows[i] = api.NetworkInterfaceDetails{ - StatusStandaloneResourceDetails: stdRows[i], + StatusInfrasResourceBaseDetails: stdRows[i], ManagedResourceInfo: manRows[i], CloudregionResourceInfo: regRows[i], } @@ -304,6 +304,8 @@ func (self *SNetworkInterface) SyncWithCloudNetworkInterface(ctx context.Context if err != nil { return err } + + SyncCloudDomain(userCred, self, provider.GetOwnerId()) db.OpsLog.LogSyncUpdate(self, diff, userCred) return nil } @@ -350,6 +352,8 @@ func (manager *SNetworkInterfaceManager) newFromCloudNetworkInterface(ctx contex return nil, errors.Wrap(err, "TableSpec().Insert(&networkinterface)") } + SyncCloudDomain(userCred, &networkinterface, provider.GetOwnerId()) + db.OpsLog.LogEvent(&networkinterface, db.ACT_CREATE, networkinterface.GetShortDesc(ctx), userCred) return &networkinterface, nil diff --git a/pkg/compute/models/networks.go b/pkg/compute/models/networks.go index 1696933fc5..5a95392959 100644 --- a/pkg/compute/models/networks.go +++ b/pkg/compute/models/networks.go @@ -85,7 +85,7 @@ type SNetwork struct { // 起始IP地址 GuestIpStart string `width:"16" charset:"ascii" nullable:"false" list:"user" update:"user" create:"required"` - // 接收IP地址 + // 结束IP地址 GuestIpEnd string `width:"16" charset:"ascii" nullable:"false" list:"user" update:"user" create:"required"` // 掩码 GuestIpMask int8 `nullable:"false" list:"user" update:"user" create:"required"` @@ -851,11 +851,10 @@ func parseNetworkInfo(userCred mcclient.TokenCredential, info *api.NetworkConfig } } net := netObj.(*SNetwork) - if net.IsPublic || - net.ProjectId == userCred.GetProjectId() || + if net.ProjectId == userCred.GetProjectId() || (db.IsDomainAllowGet(userCred, net) && net.DomainId == userCred.GetProjectDomainId()) || db.IsAdminAllowGet(userCred, net) || - utils.IsInStringArray(userCred.GetProjectId(), net.GetSharedProjects()) { + net.IsSharable(userCred) { info.Network = netObj.GetId() } else { return nil, httperrors.NewForbiddenError("no allow to access network %s", info.Network) @@ -1384,18 +1383,18 @@ func (manager *SNetworkManager) ValidateCreateData(ctx context.Context, userCred return input, nil } -func (self *SNetwork) validateUpdateData(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, data *jsonutils.JSONDict) (*jsonutils.JSONDict, error) { +func (self *SNetwork) validateUpdateData(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, input api.NetworkUpdateInput) (api.NetworkUpdateInput, error) { var startIp, endIp netutils.IPV4Addr var err error - ipStartStr, _ := data.GetString("guest_ip_start") - ipEndStr, _ := data.GetString("guest_ip_end") + ipStartStr := input.GuestIpStart + ipEndStr := input.GuestIpEnd if len(ipStartStr) > 0 || len(ipEndStr) > 0 { if len(ipStartStr) > 0 { startIp, err = netutils.NewIPV4Addr(ipStartStr) if err != nil { - return nil, httperrors.NewInputParameterError("Invalid start ip: %s %s", ipStartStr, err) + return input, httperrors.NewInputParameterError("Invalid start ip: %s %s", ipStartStr, err) } } else { startIp, _ = netutils.NewIPV4Addr(self.GuestIpStart) @@ -1403,7 +1402,7 @@ func (self *SNetwork) validateUpdateData(ctx context.Context, userCred mcclient. if len(ipEndStr) > 0 { endIp, err = netutils.NewIPV4Addr(ipEndStr) if err != nil { - return nil, httperrors.NewInputParameterError("invalid end ip: %s %s", ipEndStr, err) + return input, httperrors.NewInputParameterError("invalid end ip: %s %s", ipEndStr, err) } } else { endIp, _ = netutils.NewIPV4Addr(self.GuestIpEnd) @@ -1417,74 +1416,83 @@ func (self *SNetwork) validateUpdateData(ctx context.Context, userCred mcclient. nets := NetworkManager.getAllNetworks(self.WireId, self.Id) if nets == nil { - return nil, httperrors.NewInternalServerError("query all networks fail") + return input, httperrors.NewInternalServerError("query all networks fail") } if isOverlapNetworks(nets, startIp, endIp) { - return nil, httperrors.NewInputParameterError("Conflict address space with existing networks") + return input, httperrors.NewInputParameterError("Conflict address space with existing networks") } netRange := netutils.NewIPV4AddrRange(startIp, endIp) vpc := self.GetVpc() if !vpc.containsIPV4Range(netRange) { - return nil, httperrors.NewInputParameterError("Network not in range of VPC cidrblock %s", vpc.CidrBlock) + return input, httperrors.NewInputParameterError("Network not in range of VPC cidrblock %s", vpc.CidrBlock) } usedMap := self.GetUsedAddresses() for usedIpStr := range usedMap { usedIp, _ := netutils.NewIPV4Addr(usedIpStr) if !netRange.Contains(usedIp) { - return nil, httperrors.NewInputParameterError("Address been assigned out of new range") + return input, httperrors.NewInputParameterError("Address been assigned out of new range") } } - data.Add(jsonutils.NewString(startIp.String()), "guest_ip_start") - data.Add(jsonutils.NewString(endIp.String()), "guest_ip_end") + input.GuestIpStart = startIp.String() + input.GuestIpEnd = endIp.String() } - if data.Contains("guest_ip_mask") { - maskLen64, _ := data.Int("guest_ip_mask") + if input.GuestIpMask != nil { + maskLen64 := int64(*input.GuestIpMask) if !isValidMaskLen(maskLen64) { - return nil, httperrors.NewInputParameterError("Invalid masklen %d", maskLen64) + return input, httperrors.NewInputParameterError("Invalid masklen %d", maskLen64) } } - for _, key := range []string{"guest_gateway", "guest_dns", "guest_dhcp"} { - ipStr, _ := data.GetString(key) + for key, ipStr := range map[string]string{ + "guest_gateway": input.GuestGateway, + "guest_dns": input.GuestDns, + "guest_dhcp": input.GuestDhcp, + } { if len(ipStr) > 0 { if key == "guest_dhcp" { ipList := strings.Split(ipStr, ",") for _, ipstr := range ipList { if !regutils.MatchIPAddr(ipstr) { - return nil, httperrors.NewInputParameterError("%s: Invalid IP address %s", key, ipstr) + return input, httperrors.NewInputParameterError("%s: Invalid IP address %s", key, ipstr) } } } else if !regutils.MatchIPAddr(ipStr) { - return nil, httperrors.NewInputParameterError("%s: Invalid IP address %s", key, ipStr) + return input, httperrors.NewInputParameterError("%s: Invalid IP address %s", key, ipStr) } } } - return data, nil + return input, nil } -func (self *SNetwork) ValidateUpdateData(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, data *jsonutils.JSONDict) (*jsonutils.JSONDict, error) { +func (self *SNetwork) ValidateUpdateData(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, input api.NetworkUpdateInput) (api.NetworkUpdateInput, error) { if !self.isManaged() && !self.isOneCloudVpcNetwork() { var err error - data, err = self.validateUpdateData(ctx, userCred, query, data) + input, err = self.validateUpdateData(ctx, userCred, query, input) if err != nil { - return nil, err + return input, errors.Wrap(err, "validateUpdateData") } } else { - data.Remove("guest_ip_start") - data.Remove("guest_ip_end") - data.Remove("guest_ip_mask") - data.Remove("guest_gateway") - data.Remove("guest_dns") - data.Remove("guest_dhcp") + input.GuestIpStart = "" + input.GuestIpEnd = "" + input.GuestIpMask = nil + input.GuestGateway = "" + input.GuestDns = "" + input.GuestDomain = "" + input.GuestDhcp = "" } - return self.SSharableVirtualResourceBase.ValidateUpdateData(ctx, userCred, query, data) + var err error + input.SharableVirtualResourceBaseUpdateInput, err = self.SSharableVirtualResourceBase.ValidateUpdateData(ctx, userCred, query, input.SharableVirtualResourceBaseUpdateInput) + if err != nil { + return input, errors.Wrap(err, "SSharableVirtualResourceBase.ValidateUpdateData") + } + return input, nil } func (manager *SNetworkManager) getAllNetworks(wireId, excludeId string) []SNetwork { diff --git a/pkg/compute/models/regiondrivers.go b/pkg/compute/models/regiondrivers.go index fa02f05ce7..74615b21a0 100644 --- a/pkg/compute/models/regiondrivers.go +++ b/pkg/compute/models/regiondrivers.go @@ -85,7 +85,7 @@ type IRegionDriver interface { RequestCreateLoadbalancerListenerRule(ctx context.Context, userCred mcclient.TokenCredential, lbr *SLoadbalancerListenerRule, task taskman.ITask) error RequestDeleteLoadbalancerListenerRule(ctx context.Context, userCred mcclient.TokenCredential, lbr *SLoadbalancerListenerRule, task taskman.ITask) error - ValidateCreateVpcData(ctx context.Context, userCred mcclient.TokenCredential, data *jsonutils.JSONDict) (*jsonutils.JSONDict, error) + ValidateCreateVpcData(ctx context.Context, userCred mcclient.TokenCredential, input api.VpcCreateInput) (api.VpcCreateInput, error) ValidateCreateEipData(ctx context.Context, userCred mcclient.TokenCredential, input *api.SElasticipCreateInput) error RequestCreateVpc(ctx context.Context, userCred mcclient.TokenCredential, region *SCloudregion, vpc *SVpc, task taskman.ITask) error RequestDeleteVpc(ctx context.Context, userCred mcclient.TokenCredential, region *SCloudregion, vpc *SVpc, task taskman.ITask) error diff --git a/pkg/compute/models/routetables.go b/pkg/compute/models/routetables.go index 34574b4e25..d81df739c3 100644 --- a/pkg/compute/models/routetables.go +++ b/pkg/compute/models/routetables.go @@ -16,9 +16,6 @@ package models import ( "context" - "net" - "reflect" - "strings" "yunion.io/x/jsonutils" "yunion.io/x/log" @@ -27,7 +24,6 @@ import ( "yunion.io/x/pkg/util/compare" "yunion.io/x/sqlchemy" - "yunion.io/x/onecloud/pkg/apis" api "yunion.io/x/onecloud/pkg/apis/compute" "yunion.io/x/onecloud/pkg/cloudcommon/db" "yunion.io/x/onecloud/pkg/cloudcommon/db/lockman" @@ -38,65 +34,8 @@ import ( "yunion.io/x/onecloud/pkg/util/stringutils2" ) -type SRoute struct { - Type string - Cidr string - NextHopType string - NextHopId string -} - -func (route *SRoute) Validate(data *jsonutils.JSONDict) error { - if strings.Index(route.Cidr, "/") > 0 { - _, ipNet, err := net.ParseCIDR(route.Cidr) - if err != nil { - return err - } - // normalize from 192.168.1.3/24 to 192.168.1.0/24 - route.Cidr = ipNet.String() - } else { - ip := net.ParseIP(route.Cidr).To4() - if ip == nil { - return httperrors.NewInputParameterError("invalid addr %s", route.Cidr) - } - } - return nil -} - -type SRoutes []*SRoute - -func (routes SRoutes) String() string { - return jsonutils.Marshal(routes).String() -} -func (routes SRoutes) IsZero() bool { - if len(routes) == 0 { - return true - } - return false -} - -func (routes *SRoutes) Validate(data *jsonutils.JSONDict) error { - if routes == nil { - *routes = SRoutes{} - return nil - } - - found := map[string]struct{}{} - for _, route := range *routes { - if err := route.Validate(data); err != nil { - return err - } - if _, ok := found[route.Cidr]; ok { - // error so that the user has a chance to deal with comments - return httperrors.NewInputParameterError("duplicate route cidr %s", route.Cidr) - } - // TODO aliyun: check overlap with System type route - found[route.Cidr] = struct{}{} - } - return nil -} - type SRouteTableManager struct { - db.SVirtualResourceBaseManager + db.SStatusInfrasResourceBaseManager db.SExternalizedResourceBaseManager SVpcResourceBaseManager } @@ -104,11 +43,8 @@ type SRouteTableManager struct { var RouteTableManager *SRouteTableManager func init() { - gotypes.RegisterSerializable(reflect.TypeOf(&SRoutes{}), func() gotypes.ISerializable { - return &SRoutes{} - }) RouteTableManager = &SRouteTableManager{ - SVirtualResourceBaseManager: db.NewVirtualResourceBaseManager( + SStatusInfrasResourceBaseManager: db.NewStatusInfrasResourceBaseManager( SRouteTable{}, "route_tables_tbl", "route_table", @@ -119,12 +55,12 @@ func init() { } type SRouteTable struct { - db.SVirtualResourceBase + db.SStatusInfrasResourceBase db.SExternalizedResourceBase - SVpcResourceBase + SVpcResourceBase `create:"required"` - Type string `width:"16" charset:"ascii" nullable:"false" list:"user"` - Routes *SRoutes `list:"user" update:"user" create:"required"` + Type string `width:"16" charset:"ascii" nullable:"false" list:"user"` + Routes *api.SRoutes `list:"user" update:"user" create:"required"` } // VPC虚拟路由表列表 @@ -136,9 +72,9 @@ func (man *SRouteTableManager) ListItemFilter( ) (*sqlchemy.SQuery, error) { var err error - q, err = man.SVirtualResourceBaseManager.ListItemFilter(ctx, q, userCred, query.VirtualResourceListInput) + q, err = man.SStatusInfrasResourceBaseManager.ListItemFilter(ctx, q, userCred, query.StatusInfrasResourceBaseListInput) if err != nil { - return nil, errors.Wrap(err, "SVirtualResourceBaseManager.ListItemFilter") + return nil, errors.Wrap(err, "SStatusInfrasResourceBaseManager.ListItemFilter") } q, err = man.SExternalizedResourceBaseManager.ListItemFilter(ctx, q, userCred, query.ExternalizedResourceBaseListInput) @@ -166,9 +102,9 @@ func (man *SRouteTableManager) OrderByExtraFields( ) (*sqlchemy.SQuery, error) { var err error - q, err = man.SVirtualResourceBaseManager.OrderByExtraFields(ctx, q, userCred, query.VirtualResourceListInput) + q, err = man.SStatusInfrasResourceBaseManager.OrderByExtraFields(ctx, q, userCred, query.StatusInfrasResourceBaseListInput) if err != nil { - return nil, errors.Wrap(err, "SVirtualResourceBaseManager.OrderByExtraFields") + return nil, errors.Wrap(err, "SStatusInfrasResourceBaseManager.OrderByExtraFields") } q, err = man.SVpcResourceBaseManager.OrderByExtraFields(ctx, q, userCred, query.VpcFilterListInput) @@ -182,7 +118,7 @@ func (man *SRouteTableManager) OrderByExtraFields( func (man *SRouteTableManager) QueryDistinctExtraField(q *sqlchemy.SQuery, field string) (*sqlchemy.SQuery, error) { var err error - q, err = man.SVirtualResourceBaseManager.QueryDistinctExtraField(q, field) + q, err = man.SInfrasResourceBaseManager.QueryDistinctExtraField(q, field) if err == nil { return q, nil } @@ -195,7 +131,7 @@ func (man *SRouteTableManager) QueryDistinctExtraField(q *sqlchemy.SQuery, field } func (man *SRouteTableManager) validateRoutes(data *jsonutils.JSONDict, update bool) (*jsonutils.JSONDict, error) { - routes := SRoutes{} + routes := api.SRoutes{} routesV := validators.NewStructValidator("routes", &routes) if update { routesV.Optional(true) @@ -207,55 +143,29 @@ func (man *SRouteTableManager) validateRoutes(data *jsonutils.JSONDict, update b return data, nil } -func (man *SRouteTableManager) ValidateCreateData(ctx context.Context, userCred mcclient.TokenCredential, ownerId mcclient.IIdentityProvider, query jsonutils.JSONObject, data *jsonutils.JSONDict) (*jsonutils.JSONDict, error) { - data, err := man.validateRoutes(data, false) +func (man *SRouteTableManager) ValidateCreateData( + ctx context.Context, + userCred mcclient.TokenCredential, + ownerId mcclient.IIdentityProvider, + query jsonutils.JSONObject, + input api.RouteTableCreateInput, +) (api.RouteTableCreateInput, error) { + _, err := man.validateRoutes(jsonutils.Marshal(input).(*jsonutils.JSONDict), false) if err != nil { - return nil, err + return input, errors.Wrap(err, "validateRoutes") } - vpcV := validators.NewModelIdOrNameValidator("vpc", "vpc", ownerId) - if err := vpcV.Validate(data); err != nil { - return nil, err - } - vpc := vpcV.Model.(*SVpc) - cloudregion, err := vpc.GetRegion() + vpcObj, err := ValidateVpcResourceInput(userCred, input.VpcResourceInput) if err != nil { - return nil, httperrors.NewConflictError("failed getting region of vpc %s(%s)", vpc.Name, vpc.Id) + return input, errors.Wrap(err, "ValidateVpcResourceInput") } - data.Set("cloudregion_id", jsonutils.NewString(cloudregion.Id)) - - input := apis.VirtualResourceCreateInput{} - err = data.Unmarshal(&input) + input.Vpc = vpcObj.Id + input.StatusInfrasResourceBaseCreateInput, err = man.SStatusInfrasResourceBaseManager.ValidateCreateData(ctx, userCred, ownerId, query, input.StatusInfrasResourceBaseCreateInput) if err != nil { - return nil, httperrors.NewInternalServerError("unmarshal VirtualResourceCreateInput fail %s", err) + return input, errors.Wrap(err, "SStatusInfrasResourceBaseManager.ValidateCreateData") } - input, err = man.SVirtualResourceBaseManager.ValidateCreateData(ctx, userCred, ownerId, query, input) - if err != nil { - return nil, err - } - data.Update(jsonutils.Marshal(input)) - return data, nil + return input, nil } -/*func (manager *SRouteTableManager) QueryDistinctExtraField(q *sqlchemy.SQuery, field string) (*sqlchemy.SQuery, error) { - var err error - q, err = manager.SVirtualResourceBaseManager.QueryDistinctExtraField(q, field) - if err == nil { - return q, nil - } - switch field { - case "account": - cloudproviders := CloudproviderManager.Query().SubQuery() - cloudaccounts := CloudaccountManager.Query("name", "id").Distinct().SubQuery() - q = q.Join(cloudproviders, sqlchemy.Equals(q.Field("manager_id"), cloudproviders.Field("id"))) - q = q.Join(cloudaccounts, sqlchemy.Equals(cloudproviders.Field("cloudaccount_id"), cloudaccounts.Field("id"))) - q.GroupBy(cloudaccounts.Field("name")) - q.AppendField(cloudaccounts.Field("name", "account")) - default: - return q, httperrors.NewBadRequestError("unsupport field %s", field) - } - return q, nil -}*/ - func (rt *SRouteTable) AllowPerformPurge(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, data jsonutils.JSONObject) bool { return db.IsAdminAllowPerform(userCred, rt, "purge") } @@ -276,15 +186,24 @@ func (rt *SRouteTable) PerformPurge(ctx context.Context, userCred mcclient.Token } func (rt *SRouteTable) RealDelete(ctx context.Context, userCred mcclient.TokenCredential) error { - return rt.SVirtualResourceBase.Delete(ctx, userCred) + return rt.SStatusInfrasResourceBase.Delete(ctx, userCred) } -func (rt *SRouteTable) ValidateUpdateData(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, data *jsonutils.JSONDict) (*jsonutils.JSONDict, error) { - data, err := RouteTableManager.validateRoutes(data, true) +func (rt *SRouteTable) ValidateUpdateData( + ctx context.Context, + userCred mcclient.TokenCredential, + query jsonutils.JSONObject, + input api.RouteTableUpdateInput, +) (api.RouteTableUpdateInput, error) { + _, err := RouteTableManager.validateRoutes(jsonutils.Marshal(input).(*jsonutils.JSONDict), true) if err != nil { - return nil, err + return input, errors.Wrap(err, "RouteTableManager.validateRoutes") } - return rt.SVirtualResourceBase.ValidateUpdateData(ctx, userCred, query, data) + input.StatusInfrasResourceBaseUpdateInput, err = rt.SStatusInfrasResourceBase.ValidateUpdateData(ctx, userCred, query, input.StatusInfrasResourceBaseUpdateInput) + if err != nil { + return input, errors.Wrap(err, "SStatusInfrasResourceBase.ValidateUpdateData") + } + return input, nil } func (rt *SRouteTable) AllowPerformAddRoutes(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, data *jsonutils.JSONDict) bool { @@ -298,13 +217,13 @@ func (rt *SRouteTable) AllowPerformDelRoutes(ctx context.Context, userCred mccli // PerformAddRoutes patches acl entries by adding then deleting the specified acls. // This is intended mainly for command line operations. func (rt *SRouteTable) PerformAddRoutes(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, data *jsonutils.JSONDict) (*jsonutils.JSONDict, error) { - var routes SRoutes + var routes api.SRoutes if rt.Routes != nil { - routes_ := gotypes.DeepCopy(rt.Routes).(*SRoutes) + routes_ := gotypes.DeepCopy(rt.Routes).(*api.SRoutes) routes = *routes_ } { - adds := SRoutes{} + adds := api.SRoutes{} addsV := validators.NewStructValidator("routes", &adds) addsV.Optional(true) err := addsV.Validate(data) @@ -335,9 +254,9 @@ func (rt *SRouteTable) PerformAddRoutes(ctx context.Context, userCred mcclient.T } func (rt *SRouteTable) PerformDelRoutes(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, data *jsonutils.JSONDict) (*jsonutils.JSONDict, error) { - var routes SRoutes + var routes api.SRoutes if rt.Routes != nil { - routes_ := gotypes.DeepCopy(rt.Routes).(*SRoutes) + routes_ := gotypes.DeepCopy(rt.Routes).(*api.SRoutes) routes = *routes_ } { @@ -383,20 +302,20 @@ func (manager *SRouteTableManager) FetchCustomizeColumns( ) []api.RouteTableDetails { rows := make([]api.RouteTableDetails, len(objs)) - virtRows := manager.SVirtualResourceBaseManager.FetchCustomizeColumns(ctx, userCred, query, objs, fields, isList) + virtRows := manager.SStatusInfrasResourceBaseManager.FetchCustomizeColumns(ctx, userCred, query, objs, fields, isList) vpcRows := manager.SVpcResourceBaseManager.FetchCustomizeColumns(ctx, userCred, query, objs, fields, isList) for i := range rows { rows[i] = api.RouteTableDetails{ - VirtualResourceDetails: virtRows[i], - VpcResourceInfo: vpcRows[i], + StatusInfrasResourceBaseDetails: virtRows[i], + VpcResourceInfo: vpcRows[i], } } return rows } -func (man *SRouteTableManager) SyncRouteTables(ctx context.Context, userCred mcclient.TokenCredential, vpc *SVpc, cloudRouteTables []cloudprovider.ICloudRouteTable) ([]SRouteTable, []cloudprovider.ICloudRouteTable, compare.SyncResult) { +func (man *SRouteTableManager) SyncRouteTables(ctx context.Context, userCred mcclient.TokenCredential, vpc *SVpc, cloudRouteTables []cloudprovider.ICloudRouteTable, provider *SCloudprovider) ([]SRouteTable, []cloudprovider.ICloudRouteTable, compare.SyncResult) { lockman.LockClass(ctx, man, db.GetLockClassKey(man, userCred)) defer lockman.ReleaseClass(ctx, man, db.GetLockClassKey(man, userCred)) @@ -428,7 +347,7 @@ func (man *SRouteTableManager) SyncRouteTables(ctx context.Context, userCred mcc } for i := 0; i < len(commondb); i += 1 { - err := commondb[i].SyncWithCloudRouteTable(ctx, userCred, vpc, commonext[i]) + err := commondb[i].SyncWithCloudRouteTable(ctx, userCred, vpc, commonext[i], provider.GetOwnerId()) if err != nil { syncResult.UpdateError(err) continue @@ -440,7 +359,7 @@ func (man *SRouteTableManager) SyncRouteTables(ctx context.Context, userCred mcc } for i := 0; i < len(added); i += 1 { - routeTableNew, err := man.insertFromCloud(ctx, userCred, vpc, added[i]) + routeTableNew, err := man.insertFromCloud(ctx, userCred, vpc, added[i], provider) if err != nil { syncResult.AddError(err) continue @@ -454,14 +373,14 @@ func (man *SRouteTableManager) SyncRouteTables(ctx context.Context, userCred mcc } func (man *SRouteTableManager) newRouteTableFromCloud(userCred mcclient.TokenCredential, vpc *SVpc, cloudRouteTable cloudprovider.ICloudRouteTable) (*SRouteTable, error) { - routes := SRoutes{} + routes := api.SRoutes{} { cloudRoutes, err := cloudRouteTable.GetIRoutes() if err != nil { return nil, err } for _, cloudRoute := range cloudRoutes { - route := &SRoute{ + route := &api.SRoute{ Type: cloudRoute.GetType(), Cidr: cloudRoute.GetCidr(), NextHopType: cloudRoute.GetNextHopType(), @@ -486,8 +405,8 @@ func (man *SRouteTableManager) newRouteTableFromCloud(userCred mcclient.TokenCre // routeTable.ManagerId = vpc.ManagerId routeTable.ExternalId = cloudRouteTable.GetGlobalId() routeTable.Description = cloudRouteTable.GetDescription() - routeTable.ProjectId = userCred.GetProjectId() - routeTable.DomainId = userCred.GetProjectDomainId() + // routeTable.ProjectId = userCred.GetProjectId() + // routeTable.DomainId = userCred.GetProjectDomainId() routeTable.SetModelManager(man, routeTable) return routeTable, nil } @@ -502,7 +421,7 @@ func routeTableBasename(name, vpcName string) string { } } -func (man *SRouteTableManager) insertFromCloud(ctx context.Context, userCred mcclient.TokenCredential, vpc *SVpc, cloudRouteTable cloudprovider.ICloudRouteTable) (*SRouteTable, error) { +func (man *SRouteTableManager) insertFromCloud(ctx context.Context, userCred mcclient.TokenCredential, vpc *SVpc, cloudRouteTable cloudprovider.ICloudRouteTable, provider *SCloudprovider) (*SRouteTable, error) { routeTable, err := man.newRouteTableFromCloud(userCred, vpc, cloudRouteTable) if err != nil { return nil, err @@ -510,6 +429,7 @@ func (man *SRouteTableManager) insertFromCloud(ctx context.Context, userCred mcc if err := man.TableSpec().Insert(routeTable); err != nil { return nil, err } + SyncCloudDomain(userCred, routeTable, provider.GetOwnerId()) db.OpsLog.LogEvent(routeTable, db.ACT_CREATE, routeTable.GetShortDesc(ctx), userCred) return routeTable, nil } @@ -526,7 +446,7 @@ func (self *SRouteTable) syncRemoveCloudRouteTable(ctx context.Context, userCred return err } -func (self *SRouteTable) SyncWithCloudRouteTable(ctx context.Context, userCred mcclient.TokenCredential, vpc *SVpc, cloudRouteTable cloudprovider.ICloudRouteTable) error { +func (self *SRouteTable) SyncWithCloudRouteTable(ctx context.Context, userCred mcclient.TokenCredential, vpc *SVpc, cloudRouteTable cloudprovider.ICloudRouteTable, syncOwnerId mcclient.IIdentityProvider) error { man := self.GetModelManager().(*SRouteTableManager) routeTable, err := man.newRouteTableFromCloud(userCred, vpc, cloudRouteTable) if err != nil { @@ -542,6 +462,9 @@ func (self *SRouteTable) SyncWithCloudRouteTable(ctx context.Context, userCred m if err != nil { return err } + + SyncCloudDomain(userCred, self, syncOwnerId) + db.OpsLog.LogSyncUpdate(self, diff, userCred) return nil } diff --git a/pkg/compute/models/schedpolicies.go b/pkg/compute/models/schedpolicies.go index 3eeecbd66f..5e5cecc13e 100644 --- a/pkg/compute/models/schedpolicies.go +++ b/pkg/compute/models/schedpolicies.go @@ -107,7 +107,18 @@ func (self *SSchedpolicy) ValidateUpdateData(ctx context.Context, userCred mccli return nil, err } - return self.SStandaloneResourceBase.ValidateUpdateData(ctx, userCred, query, data) + input := apis.StandaloneResourceBaseUpdateInput{} + err = data.Unmarshal(&input) + if err != nil { + return nil, errors.Wrap(err, "Unmarshal") + } + input, err = self.SStandaloneResourceBase.ValidateUpdateData(ctx, userCred, query, input) + if err != nil { + return nil, errors.Wrap(err, "SStandaloneResourceBase.ValidateUpdateData") + } + data.Update(jsonutils.Marshal(input)) + + return data, nil } func (self *SSchedpolicy) getSchedtag() *SSchedtag { diff --git a/pkg/compute/models/schedtags.go b/pkg/compute/models/schedtags.go index e4144b3f94..d6c85d4baa 100644 --- a/pkg/compute/models/schedtags.go +++ b/pkg/compute/models/schedtags.go @@ -26,10 +26,12 @@ import ( "yunion.io/x/pkg/utils" "yunion.io/x/sqlchemy" + "yunion.io/x/onecloud/pkg/apis" api "yunion.io/x/onecloud/pkg/apis/compute" "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/rbacutils" "yunion.io/x/onecloud/pkg/util/stringutils2" ) @@ -108,6 +110,10 @@ func (manager *SSchedtagManager) InitializeData() error { return nil } +func (manager *SSchedtagManager) NamespaceScope() rbacutils.TRbacScope { + return rbacutils.ScopeSystem +} + func (manager *SSchedtagManager) BindJointManagers(ms map[db.IModelManager]ISchedtagJointManager) { for m, schedtagM := range ms { manager.jointsManager[m.KeywordPlural()] = schedtagM @@ -297,7 +303,18 @@ func (self *SSchedtag) ValidateUpdateData(ctx context.Context, userCred mcclient return nil, err } } - return self.SStandaloneResourceBase.ValidateUpdateData(ctx, userCred, query, data) + input := apis.StandaloneResourceBaseUpdateInput{} + err := data.Unmarshal(&input) + if err != nil { + return nil, errors.Wrap(err, "Unmarshal") + } + input, err = self.SStandaloneResourceBase.ValidateUpdateData(ctx, userCred, query, input) + if err != nil { + return nil, errors.Wrap(err, "SStandaloneResourceBase.ValidateUpdateData") + } + data.Update(jsonutils.Marshal(input)) + + return data, nil } func (self *SSchedtag) ValidateDeleteCondition(ctx context.Context) error { diff --git a/pkg/compute/models/secgrouprules.go b/pkg/compute/models/secgrouprules.go index 9ec84ddbfc..15b0683342 100644 --- a/pkg/compute/models/secgrouprules.go +++ b/pkg/compute/models/secgrouprules.go @@ -28,6 +28,7 @@ import ( "yunion.io/x/pkg/util/stringutils" "yunion.io/x/sqlchemy" + "yunion.io/x/onecloud/pkg/apis" api "yunion.io/x/onecloud/pkg/apis/compute" "yunion.io/x/onecloud/pkg/cloudcommon/db" "yunion.io/x/onecloud/pkg/cloudcommon/db/lockman" @@ -339,7 +340,18 @@ func (self *SSecurityGroupRule) ValidateUpdateData(ctx context.Context, userCred // 更新操作日志: 对比可以知道改了原有规则哪些内容 data.Add(jsonutils.Marshal(self), "origin") - return self.SResourceBase.ValidateUpdateData(ctx, userCred, query, data) + rinput := apis.ResourceBaseUpdateInput{} + err = data.Unmarshal(&rinput) + if err != nil { + return nil, errors.Wrap(err, "Unmarshal") + } + rinput, err = self.SResourceBase.ValidateUpdateData(ctx, userCred, query, rinput) + if err != nil { + return nil, errors.Wrap(err, "SResourceBase.ValidateUpdateData") + } + data.Update(jsonutils.Marshal(rinput)) + + return data, nil } func (self *SSecurityGroupRule) String() string { diff --git a/pkg/compute/models/skus.go b/pkg/compute/models/skus.go index 9ff3dbd5eb..5e6abad3df 100644 --- a/pkg/compute/models/skus.go +++ b/pkg/compute/models/skus.go @@ -629,16 +629,23 @@ func (self *SServerSku) AllowUpdateItem(ctx context.Context, userCred mcclient.T return inWhiteList(self.Provider) && db.IsAdminAllowUpdate(userCred, self) } -func (self *SServerSku) ValidateUpdateData(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, data *jsonutils.JSONDict) (*jsonutils.JSONDict, error) { +func (self *SServerSku) ValidateUpdateData(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, input api.ServerSkuUpdateInput) (api.ServerSkuUpdateInput, error) { // 目前不允许改sku信息 if !inWhiteList(self.Provider) { - return nil, httperrors.NewForbiddenError("can not update instance_type for public cloud %s", self.Provider) + return input, httperrors.NewForbiddenError("can not update instance_type for public cloud %s", self.Provider) } - if data.Contains("name") { - return nil, httperrors.NewUnsupportOperationError("Cannot change server sku name") + if len(input.Name) > 0 { + return input, httperrors.NewUnsupportOperationError("Cannot change server sku name") } - return self.SStatusStandaloneResourceBase.ValidateUpdateData(ctx, userCred, query, data) + + var err error + input.StatusStandaloneResourceBaseUpdateInput, err = self.SStatusStandaloneResourceBase.ValidateUpdateData(ctx, userCred, query, input.StatusStandaloneResourceBaseUpdateInput) + if err != nil { + return input, errors.Wrap(err, "SStatusStandaloneResourceBase.ValidateUpdateData") + } + + return input, nil } func (self *SServerSku) AllowDeleteItem(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, data jsonutils.JSONObject) bool { diff --git a/pkg/compute/models/storages.go b/pkg/compute/models/storages.go index da5025cbed..36004937a2 100644 --- a/pkg/compute/models/storages.go +++ b/pkg/compute/models/storages.go @@ -43,7 +43,7 @@ import ( ) type SStorageManager struct { - db.SEnabledStatusStandaloneResourceBaseManager + db.SEnabledStatusInfrasResourceBaseManager db.SExternalizedResourceBaseManager SManagedResourceBaseManager SZoneResourceBaseManager @@ -53,7 +53,7 @@ var StorageManager *SStorageManager func init() { StorageManager = &SStorageManager{ - SEnabledStatusStandaloneResourceBaseManager: db.NewEnabledStatusStandaloneResourceBaseManager( + SEnabledStatusInfrasResourceBaseManager: db.NewEnabledStatusInfrasResourceBaseManager( SStorage{}, "storages_tbl", "storage", @@ -64,7 +64,7 @@ func init() { } type SStorage struct { - db.SEnabledStatusStandaloneResourceBase `"status->default":"offline" "status->update":"admin" "enabled->default":"true"` + db.SEnabledStatusInfrasResourceBase `"status->default":"offline" "status->update":"admin" "enabled->default":"true"` db.SExternalizedResourceBase SManagedResourceBase @@ -131,7 +131,7 @@ func (self *SStorage) ValidateUpdateData(ctx context.Context, userCred mcclient. } func (self *SStorage) PostUpdate(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, data jsonutils.JSONObject) { - self.SEnabledStatusStandaloneResourceBase.PostUpdate(ctx, userCred, query, data) + self.SEnabledStatusInfrasResourceBase.PostUpdate(ctx, userCred, query, data) if data.Contains("cmtbound") || data.Contains("capacity") { hosts := self.GetAttachedHosts() @@ -162,7 +162,7 @@ func (self *SStorage) AllowDeleteItem(ctx context.Context, userCred mcclient.Tok func (self *SStorage) Delete(ctx context.Context, userCred mcclient.TokenCredential) error { DeleteResourceJointSchedtags(self, ctx, userCred) - return self.SEnabledStatusStandaloneResourceBase.Delete(ctx, userCred) + return self.SEnabledStatusInfrasResourceBase.Delete(ctx, userCred) } func (manager *SStorageManager) GetStorageTypesByHostType(hostType string) ([]string, error) { @@ -189,46 +189,53 @@ func (manager *SStorageManager) GetStorageTypesByHostType(hostType string) ([]st return storages, nil } -func (manager *SStorageManager) ValidateCreateData(ctx context.Context, userCred mcclient.TokenCredential, ownerId mcclient.IIdentityProvider, query jsonutils.JSONObject, input api.StorageCreateInput) (*jsonutils.JSONDict, error) { +func (manager *SStorageManager) ValidateCreateData( + ctx context.Context, + userCred mcclient.TokenCredential, + ownerId mcclient.IIdentityProvider, + query jsonutils.JSONObject, + input api.StorageCreateInput, +) (api.StorageCreateInput, error) { if !utils.IsInStringArray(input.StorageType, api.STORAGE_TYPES) { - return nil, httperrors.NewInputParameterError("Invalid storage type %s", input.StorageType) + return input, httperrors.NewInputParameterError("Invalid storage type %s", input.StorageType) } if !utils.IsInStringArray(input.MediumType, api.DISK_TYPES) { - return nil, httperrors.NewInputParameterError("Invalid medium type %s", input.MediumType) + return input, httperrors.NewInputParameterError("Invalid medium type %s", input.MediumType) } if len(input.Zone) == 0 { - return nil, httperrors.NewMissingParameterError("zone") + return input, httperrors.NewMissingParameterError("zone") } - zone, err := ZoneManager.FetchByIdOrName(userCred, input.Zone) + zone, err := ValidateZoneResourceInput(userCred, input.ZoneResourceInput) if err != nil { - if err == sql.ErrNoRows { - return nil, httperrors.NewResourceNotFoundError("failed to found zone %s", input.Zone) - } - return nil, httperrors.NewGeneralError(errors.Wrap(err, "ZoneManager.FetchByIdOrName")) + return input, errors.Wrap(err, "ValidateZoneResourceInput") } - input.ZoneId = zone.GetId() + input.Zone = zone.GetId() storageDirver := GetStorageDriver(input.StorageType) if storageDirver == nil { - return nil, httperrors.NewUnsupportOperationError("Not support create %s storage", input.StorageType) + return input, httperrors.NewUnsupportOperationError("Not support create %s storage", input.StorageType) } err = storageDirver.ValidateCreateData(ctx, userCred, &input) if err != nil { - return nil, err + return input, errors.Wrap(err, "storageDirver.ValidateCreateData") } - input.EnabledStatusStandaloneResourceCreateInput, err = manager.SEnabledStatusStandaloneResourceBaseManager.ValidateCreateData(ctx, userCred, ownerId, query, input.EnabledStatusStandaloneResourceCreateInput) + input.EnabledStatusInfrasResourceBaseCreateInput, err = manager.SEnabledStatusInfrasResourceBaseManager.ValidateCreateData(ctx, userCred, ownerId, query, input.EnabledStatusInfrasResourceBaseCreateInput) if err != nil { - return nil, err + return input, errors.Wrap(err, "SEnabledStatusInfrasResourceBaseManager.ValidateCreateData") } - return input.JSON(input), nil + + return input, nil } func (self *SStorage) CustomizeCreate(ctx context.Context, userCred mcclient.TokenCredential, ownerId mcclient.IIdentityProvider, query jsonutils.JSONObject, data jsonutils.JSONObject) error { self.SetEnabled(true) self.SetStatus(userCred, api.STORAGE_OFFLINE, "CustomizeCreate") - return self.SEnabledStatusStandaloneResourceBase.CustomizeCreate(ctx, userCred, ownerId, query, data) + // make storage shared to system by default + self.IsPublic = true + self.PublicScope = string(rbacutils.ScopeSystem) + return self.SEnabledStatusInfrasResourceBase.CustomizeCreate(ctx, userCred, ownerId, query, data) } func (self *SStorage) ValidateDeleteCondition(ctx context.Context) error { @@ -253,11 +260,11 @@ func (self *SStorage) ValidateDeleteCondition(ctx context.Context) error { if cnt > 0 { return httperrors.NewNotEmptyError("storage has snapshots") } - return self.SEnabledStatusStandaloneResourceBase.ValidateDeleteCondition(ctx) + return self.SEnabledStatusInfrasResourceBase.ValidateDeleteCondition(ctx) } func (self *SStorage) PostCreate(ctx context.Context, userCred mcclient.TokenCredential, ownerId mcclient.IIdentityProvider, query jsonutils.JSONObject, data jsonutils.JSONObject) { - self.SEnabledStatusStandaloneResourceBase.PostCreate(ctx, userCred, ownerId, query, data) + self.SEnabledStatusInfrasResourceBase.PostCreate(ctx, userCred, ownerId, query, data) storageDriver := GetStorageDriver(self.StorageType) if storageDriver != nil { @@ -430,12 +437,12 @@ func (manager *SStorageManager) FetchCustomizeColumns( isList bool, ) []api.StorageDetails { rows := make([]api.StorageDetails, len(objs)) - stdRows := manager.SEnabledStatusStandaloneResourceBaseManager.FetchCustomizeColumns(ctx, userCred, query, objs, fields, isList) + stdRows := manager.SEnabledStatusInfrasResourceBaseManager.FetchCustomizeColumns(ctx, userCred, query, objs, fields, isList) zoneRows := manager.SZoneResourceBaseManager.FetchCustomizeColumns(ctx, userCred, query, objs, fields, isList) manageRows := manager.SManagedResourceBaseManager.FetchCustomizeColumns(ctx, userCred, query, objs, fields, isList) for i := range rows { rows[i] = api.StorageDetails{ - EnabledStatusStandaloneResourceDetails: stdRows[i], + EnabledStatusInfrasResourceBaseDetails: stdRows[i], ZoneResourceInfo: zoneRows[i], ManagedResourceInfo: manageRows[i], } @@ -673,7 +680,7 @@ func (manager *SStorageManager) SyncStorages(ctx context.Context, userCred mccli } } for i := 0; i < len(commondb); i += 1 { - err = commondb[i].syncWithCloudStorage(ctx, userCred, commonext[i]) + err = commondb[i].syncWithCloudStorage(ctx, userCred, commonext[i], provider.GetOwnerId()) if err != nil { syncResult.UpdateError(err) } else { @@ -714,7 +721,7 @@ func (self *SStorage) syncRemoveCloudStorage(ctx context.Context, userCred mccli return err } -func (self *SStorage) syncWithCloudStorage(ctx context.Context, userCred mcclient.TokenCredential, extStorage cloudprovider.ICloudStorage) error { +func (self *SStorage) syncWithCloudStorage(ctx context.Context, userCred mcclient.TokenCredential, extStorage cloudprovider.ICloudStorage, syncOwnerId mcclient.IIdentityProvider) error { diff, err := db.UpdateWithLock(ctx, self, func() error { // self.Name = extStorage.GetName() self.Status = extStorage.GetStatus() @@ -736,6 +743,11 @@ func (self *SStorage) syncWithCloudStorage(ctx context.Context, userCred mcclien if err != nil { log.Errorf("syncWithCloudZone error %s", err) } + + if syncOwnerId != nil { + SyncCloudDomain(userCred, self, syncOwnerId) + } + db.OpsLog.LogSyncUpdate(self, diff, userCred) return err } @@ -771,6 +783,8 @@ func (manager *SStorageManager) newFromCloudStorage(ctx context.Context, userCre return nil, err } + SyncCloudDomain(userCred, &storage, provider.GetOwnerId()) + db.OpsLog.LogEvent(&storage, db.ACT_CREATE, storage.GetShortDesc(ctx), userCred) return &storage, nil @@ -1169,9 +1183,9 @@ func (manager *SStorageManager) ListItemFilter( return nil, errors.Wrap(err, "SZoneResourceBaseManager.ListItemFilter") } - q, err = manager.SEnabledStatusStandaloneResourceBaseManager.ListItemFilter(ctx, q, userCred, query.EnabledStatusStandaloneResourceListInput) + q, err = manager.SEnabledStatusInfrasResourceBaseManager.ListItemFilter(ctx, q, userCred, query.EnabledStatusInfrasResourceBaseListInput) if err != nil { - return nil, errors.Wrap(err, "SEnabledStatusStandaloneResourceBaseManager.ListItemFilter") + return nil, errors.Wrap(err, "SEnabledStatusInfrasResourceBaseManager.ListItemFilter") } if query.Share != nil && *query.Share { @@ -1217,9 +1231,9 @@ func (manager *SStorageManager) OrderByExtraFields( userCred mcclient.TokenCredential, query api.StorageListInput, ) (*sqlchemy.SQuery, error) { - q, err := manager.SEnabledStatusStandaloneResourceBaseManager.OrderByExtraFields(ctx, q, userCred, query.EnabledStatusStandaloneResourceListInput) + q, err := manager.SEnabledStatusInfrasResourceBaseManager.OrderByExtraFields(ctx, q, userCred, query.EnabledStatusInfrasResourceBaseListInput) if err != nil { - return nil, errors.Wrap(err, "SEnabledStatusStandaloneResourceBaseManager.OrderByExtraFields") + return nil, errors.Wrap(err, "SEnabledStatusInfrasResourceBaseManager.OrderByExtraFields") } q, err = manager.SZoneResourceBaseManager.OrderByExtraFields(ctx, q, userCred, query.ZonalFilterListInput) if err != nil { @@ -1235,7 +1249,7 @@ func (manager *SStorageManager) OrderByExtraFields( func (manager *SStorageManager) QueryDistinctExtraField(q *sqlchemy.SQuery, field string) (*sqlchemy.SQuery, error) { var err error - q, err = manager.SEnabledStatusStandaloneResourceBaseManager.QueryDistinctExtraField(q, field) + q, err = manager.SEnabledStatusInfrasResourceBaseManager.QueryDistinctExtraField(q, field) if err == nil { return q, nil } diff --git a/pkg/compute/models/vpcresource.go b/pkg/compute/models/vpcresource.go index c31d268d43..6771c3a53c 100644 --- a/pkg/compute/models/vpcresource.go +++ b/pkg/compute/models/vpcresource.go @@ -41,6 +41,18 @@ type SVpcResourceBaseManager struct { SManagedResourceBaseManager } +func ValidateVpcResourceInput(userCred mcclient.TokenCredential, input api.VpcResourceInput) (*SVpc, error) { + vpcObj, err := VpcManager.FetchByIdOrName(userCred, input.Vpc) + if err != nil { + if errors.Cause(err) == sql.ErrNoRows { + return nil, httperrors.NewResourceNotFoundError2(VpcManager.Keyword(), input.Vpc) + } else { + return nil, errors.Wrap(err, "VpcManager.FetchByIdOrName") + } + } + return vpcObj.(*SVpc), nil +} + func (self *SVpcResourceBase) GetVpc() *SVpc { obj, _ := VpcManager.FetchById(self.VpcId) if obj == nil { @@ -162,13 +174,9 @@ func (manager *SVpcResourceBaseManager) ListItemFilter( ) (*sqlchemy.SQuery, error) { var err error if len(query.Vpc) > 0 { - vpcObj, err := VpcManager.FetchByIdOrName(userCred, query.Vpc) + vpcObj, err := ValidateVpcResourceInput(userCred, query.VpcResourceInput) if err != nil { - if errors.Cause(err) == sql.ErrNoRows { - return nil, httperrors.NewResourceNotFoundError2(VpcManager.Keyword(), query.Vpc) - } else { - return nil, errors.Wrap(err, "VpcManager.FetchByIdOrName") - } + return nil, errors.Wrap(err, "ValidateVpcResourceInput") } q = q.Equals("vpc_id", vpcObj.GetId()) } diff --git a/pkg/compute/models/vpcs.go b/pkg/compute/models/vpcs.go index 00d49864c6..64be9e15df 100644 --- a/pkg/compute/models/vpcs.go +++ b/pkg/compute/models/vpcs.go @@ -20,6 +20,8 @@ import ( "fmt" "strings" + "yunion.io/x/onecloud/pkg/util/rbacutils" + "yunion.io/x/jsonutils" "yunion.io/x/log" "yunion.io/x/pkg/errors" @@ -39,7 +41,7 @@ import ( ) type SVpcManager struct { - db.SEnabledStatusStandaloneResourceBaseManager + db.SEnabledStatusInfrasResourceBaseManager db.SExternalizedResourceBaseManager SManagedResourceBaseManager SCloudregionResourceBaseManager @@ -50,7 +52,7 @@ var VpcManager *SVpcManager func init() { VpcManager = &SVpcManager{ - SEnabledStatusStandaloneResourceBaseManager: db.NewEnabledStatusStandaloneResourceBaseManager( + SEnabledStatusInfrasResourceBaseManager: db.NewEnabledStatusInfrasResourceBaseManager( SVpc{}, "vpcs_tbl", "vpc", @@ -61,7 +63,7 @@ func init() { } type SVpc struct { - db.SEnabledStatusStandaloneResourceBase + db.SEnabledStatusInfrasResourceBase db.SExternalizedResourceBase SManagedResourceBase @@ -105,7 +107,10 @@ func (self *SVpc) CustomizeCreate(ctx context.Context, userCred mcclient.TokenCr if len(idstr) > 0 { self.Id = idstr } - return self.SEnabledStatusStandaloneResourceBase.CustomizeCreate(ctx, userCred, ownerId, query, data) + // make vpc default to share to system + self.IsPublic = true + self.PublicScope = string(rbacutils.ScopeSystem) + return self.SEnabledStatusInfrasResourceBase.CustomizeCreate(ctx, userCred, ownerId, query, data) } func (self *SVpc) getNatgatewayQuery() *sqlchemy.SQuery { @@ -292,13 +297,13 @@ func (manager *SVpcManager) FetchCustomizeColumns( isList bool, ) []api.VpcDetails { rows := make([]api.VpcDetails, len(objs)) - stdRows := manager.SEnabledStatusStandaloneResourceBaseManager.FetchCustomizeColumns(ctx, userCred, query, objs, fields, isList) + stdRows := manager.SEnabledStatusInfrasResourceBaseManager.FetchCustomizeColumns(ctx, userCred, query, objs, fields, isList) managerRows := manager.SManagedResourceBaseManager.FetchCustomizeColumns(ctx, userCred, query, objs, fields, isList) regionRows := manager.SCloudregionResourceBaseManager.FetchCustomizeColumns(ctx, userCred, query, objs, fields, isList) globalVpcRows := manager.SGlobalVpcResourceBaseManager.FetchCustomizeColumns(ctx, userCred, query, objs, fields, isList) for i := range rows { rows[i] = api.VpcDetails{ - EnabledStatusStandaloneResourceDetails: stdRows[i], + EnabledStatusInfrasResourceBaseDetails: stdRows[i], ManagedResourceInfo: managerRows[i], CloudregionResourceInfo: regionRows[i], GlobalVpcResourceInfo: globalVpcRows[i], @@ -373,7 +378,7 @@ func (manager *SVpcManager) SyncVPCs(ctx context.Context, userCred mcclient.Toke } } for i := 0; i < len(commondb); i += 1 { - err = commondb[i].SyncWithCloudVpc(ctx, userCred, commonext[i]) + err = commondb[i].SyncWithCloudVpc(ctx, userCred, commonext[i], provider.GetOwnerId()) if err != nil { syncResult.UpdateError(err) continue @@ -382,7 +387,7 @@ func (manager *SVpcManager) SyncVPCs(ctx context.Context, userCred mcclient.Toke localVPCs = append(localVPCs, commondb[i]) remoteVPCs = append(remoteVPCs, commonext[i]) syncResult.Update() - err = commondb[i].SyncGlobalVpc(ctx, userCred) + err = commondb[i].SyncGlobalVpc(ctx, userCred, provider.GetOwnerId()) if err != nil { log.Errorf("%s(%s) sync global vpc error: %v", commondb[i].Name, commondb[i].Id, err) } @@ -397,7 +402,7 @@ func (manager *SVpcManager) SyncVPCs(ctx context.Context, userCred mcclient.Toke localVPCs = append(localVPCs, *newVpc) remoteVPCs = append(remoteVPCs, added[i]) syncResult.Add() - err = newVpc.SyncGlobalVpc(ctx, userCred) + err = newVpc.SyncGlobalVpc(ctx, userCred, provider.GetOwnerId()) if err != nil { log.Errorf("%s(%s) sync global vpc error: %v", newVpc.Name, newVpc.Id, err) } @@ -427,8 +432,10 @@ func (self *SVpc) syncRemoveCloudVpc(ctx context.Context, userCred mcclient.Toke return err } -func (self *SVpc) SyncGlobalVpc(ctx context.Context, userCred mcclient.TokenCredential) error { +func (self *SVpc) SyncGlobalVpc(ctx context.Context, userCred mcclient.TokenCredential, ownerId mcclient.IIdentityProvider) error { if len(self.GlobalvpcId) > 0 { + gv, _ := self.GetGlobalVpc() + SyncCloudDomain(userCred, gv, ownerId) return nil } region, err := self.GetRegion() @@ -471,6 +478,7 @@ func (self *SVpc) SyncGlobalVpc(ctx context.Context, userCred mcclient.TokenCred if err != nil { return errors.Wrap(err, "GlobalVpcManager.Insert") } + SyncCloudDomain(userCred, gv, ownerId) globalvpcId = gv.Id } _, err = db.Update(self, func() error { @@ -482,7 +490,7 @@ func (self *SVpc) SyncGlobalVpc(ctx context.Context, userCred mcclient.TokenCred return nil } -func (self *SVpc) SyncWithCloudVpc(ctx context.Context, userCred mcclient.TokenCredential, extVPC cloudprovider.ICloudVpc) error { +func (self *SVpc) SyncWithCloudVpc(ctx context.Context, userCred mcclient.TokenCredential, extVPC cloudprovider.ICloudVpc, syncOwnerId mcclient.IIdentityProvider) error { diff, err := db.UpdateWithLock(ctx, self, func() error { extVPC.Refresh() // self.Name = extVPC.GetName() @@ -499,6 +507,10 @@ func (self *SVpc) SyncWithCloudVpc(ctx context.Context, userCred mcclient.TokenC return err } + if syncOwnerId != nil { + SyncCloudDomain(userCred, self, syncOwnerId) + } + db.OpsLog.LogSyncUpdate(self, diff, userCred) return nil } @@ -528,6 +540,8 @@ func (manager *SVpcManager) newFromCloudVpc(ctx context.Context, userCred mcclie return nil, err } + SyncCloudDomain(userCred, &vpc, provider.GetOwnerId()) + db.OpsLog.LogEvent(&vpc, db.ACT_CREATE, vpc.GetShortDesc(ctx), userCred) return &vpc, nil @@ -578,62 +592,65 @@ func (manager *SVpcManager) InitializeData() error { return nil } -func (manager *SVpcManager) ValidateCreateData(ctx context.Context, userCred mcclient.TokenCredential, ownerId mcclient.IIdentityProvider, query jsonutils.JSONObject, data *jsonutils.JSONDict) (*jsonutils.JSONDict, error) { - regionId := jsonutils.GetAnyString(data, []string{"region", "cloudregion", "cloudregion_id"}) +func (manager *SVpcManager) ValidateCreateData( + ctx context.Context, + userCred mcclient.TokenCredential, + ownerId mcclient.IIdentityProvider, + query jsonutils.JSONObject, + input api.VpcCreateInput, +) (api.VpcCreateInput, error) { + regionId := input.Cloudregion if len(regionId) == 0 { - return nil, httperrors.NewMissingParameterError("cloudregion_id") + return input, httperrors.NewMissingParameterError("cloudregion_id") } regionObj, err := CloudregionManager.FetchByIdOrName(userCred, regionId) if err != nil { if err == sql.ErrNoRows { - return nil, httperrors.NewResourceNotFoundError2(CloudregionManager.Keyword(), regionId) + return input, httperrors.NewResourceNotFoundError2(CloudregionManager.Keyword(), regionId) } else { - return nil, httperrors.NewGeneralError(err) + return input, httperrors.NewGeneralError(err) } } region := regionObj.(*SCloudregion) - data.Add(jsonutils.NewString(region.GetId()), "cloudregion_id") + input.Cloudregion = region.Id + // data.Add(jsonutils.NewString(region.GetId()), "cloudregion_id") if region.isManaged() { - managerStr := jsonutils.GetAnyString(data, []string{"manager_id", "manager"}) + managerStr := input.Cloudprovider if len(managerStr) == 0 { - return nil, httperrors.NewMissingParameterError("manager_id") + return input, httperrors.NewMissingParameterError("manager_id") } managerObj, err := CloudproviderManager.FetchByIdOrName(userCred, managerStr) if err != nil { if err == sql.ErrNoRows { - return nil, httperrors.NewResourceNotFoundError2(CloudproviderManager.Keyword(), managerStr) + return input, httperrors.NewResourceNotFoundError2(CloudproviderManager.Keyword(), managerStr) } else { - return nil, httperrors.NewGeneralError(err) + return input, httperrors.NewGeneralError(err) } } - data.Add(jsonutils.NewString(managerObj.GetId()), "manager_id") + input.Cloudprovider = managerObj.GetId() + // data.Add(jsonutils.NewString(managerObj.GetId()), "manager_id") } else { - data.Set("status", jsonutils.NewString(api.VPC_STATUS_AVAILABLE)) + // data.Set("status", jsonutils.NewString(api.VPC_STATUS_AVAILABLE)) + input.Status = api.VPC_STATUS_AVAILABLE } - cidrBlock, _ := data.GetString("cidr_block") + cidrBlock := input.CidrBlock if len(cidrBlock) > 0 { blocks := strings.Split(cidrBlock, ",") for _, block := range blocks { _, err = netutils.NewIPV4Prefix(block) if err != nil { - return nil, httperrors.NewInputParameterError("invalid cidr_block %s", cidrBlock) + return input, httperrors.NewInputParameterError("invalid cidr_block %s", cidrBlock) } } } - input := apis.EnabledStatusStandaloneResourceCreateInput{} - err = data.Unmarshal(&input) + input.EnabledStatusInfrasResourceBaseCreateInput, err = manager.SEnabledStatusInfrasResourceBaseManager.ValidateCreateData(ctx, userCred, ownerId, query, input.EnabledStatusInfrasResourceBaseCreateInput) if err != nil { - return nil, httperrors.NewInternalServerError("unmarshal EnabledStatusStandaloneResourceCreateInput fail %s", err) + return input, err } - input, err = manager.SEnabledStatusStandaloneResourceBaseManager.ValidateCreateData(ctx, userCred, ownerId, query, input) - if err != nil { - return nil, err - } - data.Update(jsonutils.Marshal(input)) - return region.GetDriver().ValidateCreateVpcData(ctx, userCred, data) + return region.GetDriver().ValidateCreateVpcData(ctx, userCred, input) } func (self *SVpc) PostCreate(ctx context.Context, userCred mcclient.TokenCredential, ownerId mcclient.IIdentityProvider, query jsonutils.JSONObject, data jsonutils.JSONObject) { @@ -724,7 +741,7 @@ func (self *SVpc) RealDelete(ctx context.Context, userCred mcclient.TokenCredent } } - return self.SEnabledStatusStandaloneResourceBase.Delete(ctx, userCred) + return self.SEnabledStatusInfrasResourceBase.Delete(ctx, userCred) } func (self *SVpc) StartDeleteVpcTask(ctx context.Context, userCred mcclient.TokenCredential) error { @@ -798,7 +815,7 @@ func (manager *SVpcManager) ListItemFilter( ) (*sqlchemy.SQuery, error) { var err error - q, err = manager.SEnabledStatusStandaloneResourceBaseManager.ListItemFilter(ctx, q, userCred, query.EnabledStatusStandaloneResourceListInput) + q, err = manager.SEnabledStatusInfrasResourceBaseManager.ListItemFilter(ctx, q, userCred, query.EnabledStatusInfrasResourceBaseListInput) if err != nil { return nil, errors.Wrap(err, "SStatusStandaloneResourceBaseManager.ListItemFilter") } @@ -878,7 +895,7 @@ func (manager *SVpcManager) QueryDistinctExtraField(q *sqlchemy.SQuery, field st return q, nil default: var err error - q, err = manager.SEnabledStatusStandaloneResourceBaseManager.QueryDistinctExtraField(q, field) + q, err = manager.SEnabledStatusInfrasResourceBaseManager.QueryDistinctExtraField(q, field) if err == nil { return q, nil } @@ -907,9 +924,9 @@ func (manager *SVpcManager) OrderByExtraFields( userCred mcclient.TokenCredential, query api.VpcListInput, ) (*sqlchemy.SQuery, error) { - q, err := manager.SEnabledStatusStandaloneResourceBaseManager.OrderByExtraFields(ctx, q, userCred, query.EnabledStatusStandaloneResourceListInput) + q, err := manager.SEnabledStatusInfrasResourceBaseManager.OrderByExtraFields(ctx, q, userCred, query.EnabledStatusInfrasResourceBaseListInput) if err != nil { - return nil, errors.Wrap(err, "SEnabledStatusStandaloneResourceBaseManager.OrderByExtraFields") + return nil, errors.Wrap(err, "SEnabledStatusInfrasResourceBaseManager.OrderByExtraFields") } q, err = manager.SManagedResourceBaseManager.OrderByExtraFields(ctx, q, userCred, query.ManagedResourceListInput) if err != nil { @@ -926,31 +943,6 @@ func (manager *SVpcManager) OrderByExtraFields( return q, nil } -/*func (manager *SVpcManager) QueryDistinctExtraField(q *sqlchemy.SQuery, field string) (*sqlchemy.SQuery, error) { - var err error - q, err = manager.SEnabledStatusStandaloneResourceBaseManager.QueryDistinctExtraField(q, field) - if err == nil { - return q, nil - } - switch field { - case "account": - cloudproviders := CloudproviderManager.Query().SubQuery() - cloudaccounts := CloudaccountManager.Query("name", "id").Distinct().SubQuery() - q = q.Join(cloudproviders, sqlchemy.Equals(q.Field("manager_id"), cloudproviders.Field("id"))) - q = q.Join(cloudaccounts, sqlchemy.Equals(cloudproviders.Field("cloudaccount_id"), cloudaccounts.Field("id"))) - q.GroupBy(cloudaccounts.Field("name")) - q.AppendField(cloudaccounts.Field("name", "account")) - case "manager": - cloudproviders := CloudproviderManager.Query("name", "id").Distinct().SubQuery() - q = q.Join(cloudproviders, sqlchemy.Equals(q.Field("manager_id"), cloudproviders.Field("id"))) - q.GroupBy(cloudproviders.Field("name")) - q.AppendField(cloudproviders.Field("name", "manager")) - default: - return q, httperrors.NewBadRequestError("unsupport field %s", field) - } - return q, nil -}*/ - func (self *SVpc) SyncRemoteWires(ctx context.Context, userCred mcclient.TokenCredential) error { ivpc, err := self.GetIVpc() if err != nil { diff --git a/pkg/compute/models/wires.go b/pkg/compute/models/wires.go index 8e9a4aef0c..28f4553d1f 100644 --- a/pkg/compute/models/wires.go +++ b/pkg/compute/models/wires.go @@ -41,7 +41,7 @@ import ( ) type SWireManager struct { - db.SStandaloneResourceBaseManager + db.SInfrasResourceBaseManager db.SExternalizedResourceBaseManager SVpcResourceBaseManager SZoneResourceBaseManager @@ -51,7 +51,7 @@ var WireManager *SWireManager func init() { WireManager = &SWireManager{ - SStandaloneResourceBaseManager: db.NewStandaloneResourceBaseManager( + SInfrasResourceBaseManager: db.NewInfrasResourceBaseManager( SWire{}, "wires_tbl", "wire", @@ -62,7 +62,7 @@ func init() { } type SWire struct { - db.SStandaloneResourceBase + db.SInfrasResourceBase db.SExternalizedResourceBase SVpcResourceBase `wdith:"36" charset:"ascii" nullable:"false" list:"admin" create:"admin_required"` @@ -90,68 +90,53 @@ func (manager *SWireManager) GetContextManagers() [][]db.IModelManager { } } -func (manager *SWireManager) ValidateCreateData(ctx context.Context, userCred mcclient.TokenCredential, ownerId mcclient.IIdentityProvider, query jsonutils.JSONObject, input api.WireCreateInput) (*jsonutils.JSONDict, error) { +func (manager *SWireManager) ValidateCreateData( + ctx context.Context, + userCred mcclient.TokenCredential, + ownerId mcclient.IIdentityProvider, + query jsonutils.JSONObject, + input api.WireCreateInput, +) (api.WireCreateInput, error) { if input.Bandwidth < 0 { - return nil, httperrors.NewOutOfRangeError("bandwidth must be greater than 0") + return input, httperrors.NewOutOfRangeError("bandwidth must be greater than 0") } if input.Mtu < 0 || input.Mtu > 1000000 { - return nil, httperrors.NewOutOfRangeError("mtu must be range of 0~1000000") + return input, httperrors.NewOutOfRangeError("mtu must be range of 0~1000000") } - for _, vpc := range []string{input.Vpc, input.VpcId} { - if len(vpc) > 0 { - input.Vpc = vpc - break - } - } if len(input.Vpc) == 0 { - return nil, httperrors.NewMissingParameterError("vpc") + return input, httperrors.NewMissingParameterError("vpc") } - _vpc, err := VpcManager.FetchByIdOrName(userCred, input.Vpc) + vpc, err := ValidateVpcResourceInput(userCred, input.VpcResourceInput) if err != nil { - if err == sql.ErrNoRows { - return nil, httperrors.NewNotFoundError("Vpc %s not found", input.Vpc) - } else { - return nil, httperrors.NewInternalServerError("Fetch Vpc %s error %s", input.Vpc, err) - } + return input, errors.Wrap(err, "ValidateVpcResourceInput") } - vpc := _vpc.(*SVpc) - input.VpcId = vpc.Id + input.Vpc = vpc.Id if len(vpc.ManagerId) > 0 { - return nil, httperrors.NewNotSupportedError("Currently only kvm platform supports creating wire") - } - - for _, zone := range []string{input.Zone, input.ZoneId} { - if len(zone) > 0 { - input.Zone = zone - break - } + return input, httperrors.NewNotSupportedError("Currently only kvm platform supports creating wire") } if len(input.Zone) == 0 { - return nil, httperrors.NewMissingParameterError("zone") + return input, httperrors.NewMissingParameterError("zone") } - zone, err := ZoneManager.FetchByIdOrName(nil, input.Zone) + zone, err := ValidateZoneResourceInput(userCred, input.ZoneResourceInput) if err != nil { - if err == sql.ErrNoRows { - return nil, httperrors.NewNotFoundError("Zone %s not found", input.Vpc) - } else { - return nil, httperrors.NewInternalServerError("Fetch Zone %s error %s", input.Vpc, err) - } + return input, errors.Wrap(err, "ValidateZoneResourceInput") } - input.ZoneId = zone.GetId() + input.Zone = zone.GetId() - input.StandaloneResourceCreateInput, err = manager.SStandaloneResourceBaseManager.ValidateCreateData(ctx, userCred, ownerId, query, input.StandaloneResourceCreateInput) + input.InfrasResourceBaseCreateInput, err = manager.SInfrasResourceBaseManager.ValidateCreateData(ctx, userCred, ownerId, query, input.InfrasResourceBaseCreateInput) if err != nil { - return nil, err + return input, err } - return input.JSON(input), nil + return input, nil } -func (wire *SWire) ValidateUpdateData(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, data *jsonutils.JSONDict) (*jsonutils.JSONDict, error) { +func (wire *SWire) ValidateUpdateData(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, input api.WireUpdateInput) (api.WireUpdateInput, error) { + data := jsonutils.Marshal(input).(*jsonutils.JSONDict) keysV := []validators.IValidator{ validators.NewNonNegativeValidator("bandwidth"), validators.NewRangeValidator("mtu", 1, 1000000).Optional(true), @@ -159,11 +144,16 @@ func (wire *SWire) ValidateUpdateData(ctx context.Context, userCred mcclient.Tok for _, v := range keysV { v.Optional(true) if err := v.Validate(data); err != nil { - return nil, err + return input, err } } + var err error + input.InfrasResourceBaseUpdateInput, err = wire.SInfrasResourceBase.ValidateUpdateData(ctx, userCred, query, input.InfrasResourceBaseUpdateInput) + if err != nil { + return input, errors.Wrap(err, "SInfrasResourceBase.ValidateUpdateData") + } - return wire.SStandaloneResourceBase.ValidateUpdateData(ctx, userCred, query, data) + return input, nil } func (wire *SWire) ValidateDeleteCondition(ctx context.Context) error { @@ -181,7 +171,7 @@ func (wire *SWire) ValidateDeleteCondition(ctx context.Context) error { if cnt > 0 { return httperrors.NewNotEmptyError("wire contains networks") } - return wire.SStandaloneResourceBase.ValidateDeleteCondition(ctx) + return wire.SInfrasResourceBase.ValidateDeleteCondition(ctx) } func (manager *SWireManager) getWireExternalIdForClassicNetwork(provider string, vpcId string, zoneId string) string { @@ -273,7 +263,7 @@ func (manager *SWireManager) getWiresByVpcAndZone(vpc *SVpc, zone *SZone) ([]SWi return wires, nil } -func (manager *SWireManager) SyncWires(ctx context.Context, userCred mcclient.TokenCredential, vpc *SVpc, wires []cloudprovider.ICloudWire) ([]SWire, []cloudprovider.ICloudWire, compare.SyncResult) { +func (manager *SWireManager) SyncWires(ctx context.Context, userCred mcclient.TokenCredential, vpc *SVpc, wires []cloudprovider.ICloudWire, provider *SCloudprovider) ([]SWire, []cloudprovider.ICloudWire, compare.SyncResult) { lockman.LockClass(ctx, manager, db.GetLockClassKey(manager, userCred)) defer lockman.ReleaseClass(ctx, manager, db.GetLockClassKey(manager, userCred)) @@ -314,7 +304,7 @@ func (manager *SWireManager) SyncWires(ctx context.Context, userCred mcclient.To } } for i := 0; i < len(commondb); i += 1 { - err = commondb[i].syncWithCloudWire(ctx, userCred, commonext[i]) + err = commondb[i].syncWithCloudWire(ctx, userCred, commonext[i], provider.GetOwnerId()) if err != nil { syncResult.UpdateError(err) } else { @@ -325,7 +315,7 @@ func (manager *SWireManager) SyncWires(ctx context.Context, userCred mcclient.To } } for i := 0; i < len(added); i += 1 { - new, err := manager.newFromCloudWire(ctx, userCred, added[i], vpc) + new, err := manager.newFromCloudWire(ctx, userCred, added[i], vpc, provider.GetOwnerId()) if err != nil { syncResult.AddError(err) } else { @@ -358,7 +348,7 @@ func (self *SWire) syncRemoveCloudWire(ctx context.Context, userCred mcclient.To return err } -func (self *SWire) syncWithCloudWire(ctx context.Context, userCred mcclient.TokenCredential, extWire cloudprovider.ICloudWire) error { +func (self *SWire) syncWithCloudWire(ctx context.Context, userCred mcclient.TokenCredential, extWire cloudprovider.ICloudWire, syncOwnerId mcclient.IIdentityProvider) error { diff, err := db.UpdateWithLock(ctx, self, func() error { // self.Name = extWire.GetName() self.Bandwidth = extWire.GetBandwidth() // 10G @@ -370,6 +360,9 @@ func (self *SWire) syncWithCloudWire(ctx context.Context, userCred mcclient.Toke if err != nil { log.Errorf("syncWithCloudWire error %s", err) } + + SyncCloudDomain(userCred, self, syncOwnerId) + db.OpsLog.LogSyncUpdate(self, diff, userCred) return err } @@ -385,7 +378,7 @@ func (self *SWire) markNetworkUnknown(userCred mcclient.TokenCredential) error { return nil } -func (manager *SWireManager) newFromCloudWire(ctx context.Context, userCred mcclient.TokenCredential, extWire cloudprovider.ICloudWire, vpc *SVpc) (*SWire, error) { +func (manager *SWireManager) newFromCloudWire(ctx context.Context, userCred mcclient.TokenCredential, extWire cloudprovider.ICloudWire, vpc *SVpc, syncOwnerId mcclient.IIdentityProvider) (*SWire, error) { wire := SWire{} wire.SetModelManager(manager, &wire) @@ -414,6 +407,8 @@ func (manager *SWireManager) newFromCloudWire(ctx context.Context, userCred mccl return nil, err } + SyncCloudDomain(userCred, &wire, syncOwnerId) + db.OpsLog.LogEvent(&wire, db.ACT_CREATE, wire.GetShortDesc(ctx), userCred) return &wire, nil } @@ -831,9 +826,9 @@ func (manager *SWireManager) ListItemFilter( return nil, errors.Wrap(err, "SZoneResourceBaseManager.ListItemFilter") } - q, err = manager.SStandaloneResourceBaseManager.ListItemFilter(ctx, q, userCred, query.StandaloneResourceListInput) + q, err = manager.SInfrasResourceBaseManager.ListItemFilter(ctx, q, userCred, query.InfrasResourceBaseListInput) if err != nil { - return nil, errors.Wrap(err, "SStandaloneResourceBaseManager.ListItemFilter") + return nil, errors.Wrap(err, "SInfrasResourceBaseManager.ListItemFilter") } hostStr := query.Host @@ -857,9 +852,9 @@ func (manager *SWireManager) OrderByExtraFields( ) (*sqlchemy.SQuery, error) { var err error - q, err = manager.SStandaloneResourceBaseManager.OrderByExtraFields(ctx, q, userCred, query.StandaloneResourceListInput) + q, err = manager.SInfrasResourceBaseManager.OrderByExtraFields(ctx, q, userCred, query.InfrasResourceBaseListInput) if err != nil { - return nil, errors.Wrap(err, "SStandaloneResourceBaseManager.OrderByExtraFields") + return nil, errors.Wrap(err, "SInfrasResourceBaseManager.OrderByExtraFields") } q, err = manager.SVpcResourceBaseManager.OrderByExtraFields(ctx, q, userCred, query.VpcFilterListInput) if err != nil { @@ -879,7 +874,7 @@ func (manager *SWireManager) OrderByExtraFields( func (manager *SWireManager) QueryDistinctExtraField(q *sqlchemy.SQuery, field string) (*sqlchemy.SQuery, error) { var err error - q, err = manager.SStandaloneResourceBaseManager.QueryDistinctExtraField(q, field) + q, err = manager.SInfrasResourceBaseManager.QueryDistinctExtraField(q, field) if err == nil { return q, nil } @@ -924,13 +919,13 @@ func (manager *SWireManager) FetchCustomizeColumns( ) []api.WireDetails { rows := make([]api.WireDetails, len(objs)) - stdRows := manager.SStandaloneResourceBaseManager.FetchCustomizeColumns(ctx, userCred, query, objs, fields, isList) + stdRows := manager.SInfrasResourceBaseManager.FetchCustomizeColumns(ctx, userCred, query, objs, fields, isList) vpcRows := manager.SVpcResourceBaseManager.FetchCustomizeColumns(ctx, userCred, query, objs, fields, isList) zoneRows := manager.SZoneResourceBaseManager.FetchCustomizeColumns(ctx, userCred, query, objs, fields, isList) for i := range rows { rows[i] = api.WireDetails{ - StandaloneResourceDetails: stdRows[i], + InfrasResourceBaseDetails: stdRows[i], VpcResourceInfo: vpcRows[i], ZoneResourceInfoBase: zoneRows[i].ZoneResourceInfoBase, } @@ -957,3 +952,10 @@ func (man *SWireManager) removeWiresByVpc(ctx context.Context, userCred mcclient } return errors.NewAggregate(errs) } + +func (model *SWire) CustomizeCreate(ctx context.Context, userCred mcclient.TokenCredential, ownerId mcclient.IIdentityProvider, query jsonutils.JSONObject, data jsonutils.JSONObject) error { + // make host default public + model.IsPublic = true + model.PublicScope = string(rbacutils.ScopeSystem) + return model.SInfrasResourceBase.CustomizeCreate(ctx, userCred, ownerId, query, data) +} diff --git a/pkg/compute/models/zoneresource.go b/pkg/compute/models/zoneresource.go index 46876e7498..dc154d7c04 100644 --- a/pkg/compute/models/zoneresource.go +++ b/pkg/compute/models/zoneresource.go @@ -16,6 +16,7 @@ package models import ( "context" + "database/sql" "yunion.io/x/jsonutils" "yunion.io/x/log" @@ -38,6 +39,18 @@ type SZoneResourceBaseManager struct { SCloudregionResourceBaseManager } +func ValidateZoneResourceInput(userCred mcclient.TokenCredential, query api.ZoneResourceInput) (*SZone, error) { + zoneObj, err := ZoneManager.FetchByIdOrName(userCred, query.Zone) + if err != nil { + if err == sql.ErrNoRows { + return nil, httperrors.NewResourceNotFoundError2(ZoneManager.Keyword(), query.Zone) + } else { + return nil, httperrors.NewGeneralError(err) + } + } + return zoneObj.(*SZone), nil +} + func (self *SZoneResourceBase) GetZone() *SZone { return ZoneManager.FetchZoneById(self.ZoneId) } diff --git a/pkg/compute/regiondrivers/google.go b/pkg/compute/regiondrivers/google.go index da8a7c4838..26bdc82c8e 100644 --- a/pkg/compute/regiondrivers/google.go +++ b/pkg/compute/regiondrivers/google.go @@ -85,7 +85,7 @@ func (self *SGoogleRegionDriver) RequestCreateVpc(ctx context.Context, userCred region.SyncVpcs(ctx, userCred, iregion, provider) } - err = vpc.SyncWithCloudVpc(ctx, userCred, ivpc) + err = vpc.SyncWithCloudVpc(ctx, userCred, ivpc, nil) if err != nil { return nil, errors.Wrap(err, "vpc.SyncWithCloudVpc") } diff --git a/pkg/compute/regiondrivers/kvm.go b/pkg/compute/regiondrivers/kvm.go index a62c037870..13f74e7e0f 100644 --- a/pkg/compute/regiondrivers/kvm.go +++ b/pkg/compute/regiondrivers/kvm.go @@ -792,13 +792,13 @@ func (self *SKVMRegionDriver) RequestDeleteLoadbalancerListenerRule(ctx context. return nil } -func (self *SKVMRegionDriver) ValidateCreateVpcData(ctx context.Context, userCred mcclient.TokenCredential, data *jsonutils.JSONDict) (*jsonutils.JSONDict, error) { +func (self *SKVMRegionDriver) ValidateCreateVpcData(ctx context.Context, userCred mcclient.TokenCredential, input api.VpcCreateInput) (api.VpcCreateInput, error) { cidrChoices := choices.NewChoices("192.168.0.0/16", "10.0.0.0/8", "172.16.0.0/12") cidrV := validators.NewStringChoicesValidator("cidr_block", cidrChoices) - if err := cidrV.Validate(data); err != nil { - return nil, err + if err := cidrV.Validate(jsonutils.Marshal(input).(*jsonutils.JSONDict)); err != nil { + return input, err } - return data, nil + return input, nil } func (self *SKVMRegionDriver) RequestDeleteVpc(ctx context.Context, userCred mcclient.TokenCredential, region *models.SCloudregion, vpc *models.SVpc, task taskman.ITask) error { diff --git a/pkg/compute/regiondrivers/managedvirtual.go b/pkg/compute/regiondrivers/managedvirtual.go index 249f50a93e..7cfcecf29c 100644 --- a/pkg/compute/regiondrivers/managedvirtual.go +++ b/pkg/compute/regiondrivers/managedvirtual.go @@ -1093,8 +1093,8 @@ func (self *SManagedVirtualizationRegionDriver) RequestDeleteLoadbalancerListene return nil } -func (self *SManagedVirtualizationRegionDriver) ValidateCreateVpcData(ctx context.Context, userCred mcclient.TokenCredential, data *jsonutils.JSONDict) (*jsonutils.JSONDict, error) { - return data, nil +func (self *SManagedVirtualizationRegionDriver) ValidateCreateVpcData(ctx context.Context, userCred mcclient.TokenCredential, input api.VpcCreateInput) (api.VpcCreateInput, error) { + return input, nil } func (self *SManagedVirtualizationRegionDriver) ValidateCreateEipData(ctx context.Context, userCred mcclient.TokenCredential, input *api.SElasticipCreateInput) error { @@ -1118,7 +1118,7 @@ func (self *SManagedVirtualizationRegionDriver) RequestCreateVpc(ctx context.Con return nil, errors.Wrap(err, "cloudprovider.WaitStatus") } - err = vpc.SyncWithCloudVpc(ctx, userCred, ivpc) + err = vpc.SyncWithCloudVpc(ctx, userCred, ivpc, nil) if err != nil { return nil, errors.Wrap(err, "vpc.SyncWithCloudVpc") } diff --git a/pkg/compute/regiondrivers/qcloud.go b/pkg/compute/regiondrivers/qcloud.go index 595cf054c5..57dea58f42 100644 --- a/pkg/compute/regiondrivers/qcloud.go +++ b/pkg/compute/regiondrivers/qcloud.go @@ -687,15 +687,15 @@ func (self *SQcloudRegionDriver) RequestCreateLoadbalancerListenerRule(ctx conte return nil } -func (self *SQcloudRegionDriver) ValidateCreateVpcData(ctx context.Context, userCred mcclient.TokenCredential, data *jsonutils.JSONDict) (*jsonutils.JSONDict, error) { +func (self *SQcloudRegionDriver) ValidateCreateVpcData(ctx context.Context, userCred mcclient.TokenCredential, input api.VpcCreateInput) (api.VpcCreateInput, error) { cidrV := validators.NewIPv4PrefixValidator("cidr_block") - if err := cidrV.Validate(data); err != nil { - return nil, err + if err := cidrV.Validate(jsonutils.Marshal(input).(*jsonutils.JSONDict)); err != nil { + return input, err } if cidrV.Value.MaskLen < 16 || cidrV.Value.MaskLen > 28 { - return nil, httperrors.NewInputParameterError("%s request the mask range should be between 16 and 28", self.GetProvider()) + return input, httperrors.NewInputParameterError("%s request the mask range should be between 16 and 28", self.GetProvider()) } - return data, nil + return input, nil } func (self *SQcloudRegionDriver) ValidateUpdateLoadbalancerListenerData(ctx context.Context, userCred mcclient.TokenCredential, data *jsonutils.JSONDict, lblis *models.SLoadbalancerListener, backendGroup db.IModel) (*jsonutils.JSONDict, error) { diff --git a/pkg/compute/tasks/vpc_syncstatus_task.go b/pkg/compute/tasks/vpc_syncstatus_task.go index a3393fab9f..43f3e03037 100644 --- a/pkg/compute/tasks/vpc_syncstatus_task.go +++ b/pkg/compute/tasks/vpc_syncstatus_task.go @@ -59,7 +59,7 @@ func (self *VpcSyncstatusTask) OnInit(ctx context.Context, obj db.IStandaloneMod return } - err = vpc.SyncWithCloudVpc(ctx, self.UserCred, extVpc) + err = vpc.SyncWithCloudVpc(ctx, self.UserCred, extVpc, nil) if err != nil { msg := fmt.Sprintf("fail to sync vpc status %s", err) self.taskFail(ctx, vpc, msg) diff --git a/pkg/image/models/image_guest.go b/pkg/image/models/image_guest.go index 3bf32bbfed..a5082f405b 100644 --- a/pkg/image/models/image_guest.go +++ b/pkg/image/models/image_guest.go @@ -525,9 +525,12 @@ func (self *SGuestImageManager) CleanPendingDeleteImages(ctx context.Context, us } } -func (self *SGuestImage) PerformPublic(ctx context.Context, userCred mcclient.TokenCredential, - query jsonutils.JSONObject, input apis.PerformProjectPublicInput) (jsonutils.JSONObject, error) { - +func (self *SGuestImage) PerformPublic( + ctx context.Context, + userCred mcclient.TokenCredential, + query jsonutils.JSONObject, + input apis.PerformPublicInput, +) (jsonutils.JSONObject, error) { images, err := GuestImageJointManager.GetImagesByGuestImageId(self.Id) if err != nil { return nil, errors.Wrap(err, "fail to fetch subimages of guest image") @@ -541,9 +544,12 @@ func (self *SGuestImage) PerformPublic(ctx context.Context, userCred mcclient.To return self.SSharableVirtualResourceBase.PerformPublic(ctx, userCred, query, input) } -func (self *SGuestImage) PerformPrivate(ctx context.Context, userCred mcclient.TokenCredential, - query jsonutils.JSONObject, input apis.PerformProjectPrivateInput) (jsonutils.JSONObject, error) { - +func (self *SGuestImage) PerformPrivate( + ctx context.Context, + userCred mcclient.TokenCredential, + query jsonutils.JSONObject, + input apis.PerformPrivateInput, +) (jsonutils.JSONObject, error) { images, err := GuestImageJointManager.GetImagesByGuestImageId(self.Id) if err != nil { return nil, errors.Wrap(err, "fail to fetch subimages of guest image") diff --git a/pkg/image/models/images.go b/pkg/image/models/images.go index c6da416125..2256b98d40 100644 --- a/pkg/image/models/images.go +++ b/pkg/image/models/images.go @@ -651,7 +651,18 @@ func (self *SImage) ValidateUpdateData(ctx context.Context, userCred mcclient.To } } } - return self.SVirtualResourceBase.ValidateUpdateData(ctx, userCred, query, data) + input := apis.VirtualResourceBaseUpdateInput{} + err := data.Unmarshal(&input) + if err != nil { + return nil, errors.Wrap(err, "Unmarshal") + } + input, err = self.SVirtualResourceBase.ValidateUpdateData(ctx, userCred, query, input) + if err != nil { + return nil, errors.Wrap(err, "SVirtualResourceBase.ValidateUpdateData") + } + data.Update(jsonutils.Marshal(input)) + + return data, nil } func (self *SImage) PreUpdate(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, data jsonutils.JSONObject) { @@ -691,7 +702,7 @@ func (self *SImage) ValidateDeleteCondition(ctx context.Context) error { if self.IsGuestImage.IsTrue() { return httperrors.NewForbiddenError("image is the part of guest image") } - if self.IsPublic || len(self.GetSharedProjects()) > 0 { + if self.IsShared() { return httperrors.NewForbiddenError("image is shared") } return self.SVirtualResourceBase.ValidateDeleteCondition(ctx) @@ -1338,7 +1349,7 @@ func (self *SImage) PerformMarkStandard( ) (jsonutils.JSONObject, error) { isStandard := jsonutils.QueryBoolean(data, "is_standard", false) if !self.IsStandard.IsTrue() && isStandard { - input := apis.PerformProjectPublicInput{ + input := apis.PerformPublicInput{ Scope: "system", } _, err := self.PerformPublic(ctx, userCred, query, input) diff --git a/pkg/keystone/models/credentials.go b/pkg/keystone/models/credentials.go index 3321c40cb1..f524dd7a2b 100644 --- a/pkg/keystone/models/credentials.go +++ b/pkg/keystone/models/credentials.go @@ -164,9 +164,15 @@ func (self *SCredential) ValidateDeleteCondition(ctx context.Context) error { return self.SStandaloneResourceBase.ValidateDeleteCondition(ctx) } -func (self *SCredential) ValidateUpdateData(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, data *jsonutils.JSONDict) (*jsonutils.JSONDict, error) { +func (self *SCredential) ValidateUpdateData(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, input api.CredentialUpdateInput) (api.CredentialUpdateInput, error) { + var err error - return self.SStandaloneResourceBase.ValidateUpdateData(ctx, userCred, query, data) + input.StandaloneResourceBaseUpdateInput, err = self.SStandaloneResourceBase.ValidateUpdateData(ctx, userCred, query, input.StandaloneResourceBaseUpdateInput) + if err != nil { + return input, errors.Wrap(err, "SStandaloneResourceBase.ValidateUpdateData") + } + + return input, nil } func (self *SCredential) GetExtraDetails( diff --git a/pkg/keystone/models/domains.go b/pkg/keystone/models/domains.go index 8cd5c290a0..2e4164582d 100644 --- a/pkg/keystone/models/domains.go +++ b/pkg/keystone/models/domains.go @@ -293,17 +293,23 @@ func (domain *SDomain) ValidateUpdateCondition(ctx context.Context) error { return domain.SStandaloneResourceBase.ValidateUpdateCondition(ctx) } -func (domain *SDomain) ValidateUpdateData(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, data *jsonutils.JSONDict) (*jsonutils.JSONDict, error) { +func (domain *SDomain) ValidateUpdateData(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, input api.DomainUpdateInput) (api.DomainUpdateInput, error) { + data := jsonutils.Marshal(input) if domain.IsReadOnly() { for _, k := range []string{ "name", } { if data.Contains(k) { - return nil, httperrors.NewForbiddenError("field %s is readonly", k) + return input, httperrors.NewForbiddenError("field %s is readonly", k) } } } - return domain.SStandaloneResourceBase.ValidateUpdateData(ctx, userCred, query, data) + var err error + input.StandaloneResourceBaseUpdateInput, err = domain.SStandaloneResourceBase.ValidateUpdateData(ctx, userCred, query, input.StandaloneResourceBaseUpdateInput) + if err != nil { + return input, errors.Wrap(err, "SStandaloneResourceBase.ValidateUpdateData") + } + return input, nil } func (domain *SDomain) GetExtraDetails( diff --git a/pkg/keystone/models/groups.go b/pkg/keystone/models/groups.go index cee515f804..03cd85a589 100644 --- a/pkg/keystone/models/groups.go +++ b/pkg/keystone/models/groups.go @@ -265,18 +265,26 @@ func (group *SGroup) ValidateUpdateCondition(ctx context.Context) error { return group.SIdentityBaseResource.ValidateUpdateCondition(ctx) } -func (group *SGroup) ValidateUpdateData(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, data *jsonutils.JSONDict) (*jsonutils.JSONDict, error) { +func (group *SGroup) ValidateUpdateData(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, input api.GroupUpdateInput) (api.GroupUpdateInput, error) { + data := jsonutils.Marshal(input) if group.IsReadOnly() { for _, k := range []string{ "name", "displayname", } { if data.Contains(k) { - return nil, httperrors.NewForbiddenError("field %s is readonly", k) + return input, httperrors.NewForbiddenError("field %s is readonly", k) } } } - return group.SIdentityBaseResource.ValidateUpdateData(ctx, userCred, query, data) + + var err error + input.IdentityBaseUpdateInput, err = group.SIdentityBaseResource.ValidateUpdateData(ctx, userCred, query, input.IdentityBaseUpdateInput) + if err != nil { + return input, errors.Wrap(err, "SIdentityBaseResource.ValidateUpdateData") + } + + return input, nil } func (manager *SGroupManager) fetchGroupById(gid string) *SGroup { diff --git a/pkg/keystone/models/identity_provider.go b/pkg/keystone/models/identity_provider.go index 75e2f110e5..dc1e4b2d45 100644 --- a/pkg/keystone/models/identity_provider.go +++ b/pkg/keystone/models/identity_provider.go @@ -446,14 +446,21 @@ func (self *SIdentityProvider) getMoreDetails(out api.IdentityProviderDetails) a return out } -func (self *SIdentityProvider) ValidateUpdateData(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, data *jsonutils.JSONDict) (*jsonutils.JSONDict, error) { - if data.Contains("sync_interval_seconds") { - secs, _ := data.Int("sync_interval_seconds") +func (self *SIdentityProvider) ValidateUpdateData(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, input api.IdentityProviderUpdateInput) (api.IdentityProviderUpdateInput, error) { + if input.SyncIntervalSeconds != nil { + secs := *input.SyncIntervalSeconds if secs < api.MinimalSyncIntervalSeconds { - data.Set("sync_interval_seconds", jsonutils.NewInt(int64(api.MinimalSyncIntervalSeconds))) + secs = api.MinimalSyncIntervalSeconds + input.SyncIntervalSeconds = &secs } } - return self.SEnabledStatusStandaloneResourceBase.ValidateUpdateData(ctx, userCred, query, data) + var err error + input.EnabledStatusStandaloneResourceBaseUpdateInput, err = self.SEnabledStatusStandaloneResourceBase.ValidateUpdateData(ctx, userCred, query, input.EnabledStatusStandaloneResourceBaseUpdateInput) + if err != nil { + return input, errors.Wrap(err, "SEnabledStatusStandaloneResourceBase.ValidateUpdateData") + } + + return input, nil } func (self *SIdentityProvider) GetUserCount() (int, error) { diff --git a/pkg/keystone/models/identitybase.go b/pkg/keystone/models/identitybase.go index 2457040b5d..47dad3f6ce 100644 --- a/pkg/keystone/models/identitybase.go +++ b/pkg/keystone/models/identitybase.go @@ -31,7 +31,6 @@ import ( "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/rbacutils" "yunion.io/x/onecloud/pkg/util/stringutils2" ) @@ -267,10 +266,38 @@ func (manager *SEnabledIdentityBaseResourceManager) ValidateCreateData(ctx conte return input, nil } -func (manager *SIdentityBaseResourceManager) NamespaceScope() rbacutils.TRbacScope { - return rbacutils.ScopeSystem +func (model *SIdentityBaseResource) ValidateUpdateData( + ctx context.Context, + userCred mcclient.TokenCredential, + query jsonutils.JSONObject, + input api.IdentityBaseUpdateInput, +) (api.IdentityBaseUpdateInput, error) { + var err error + input.StandaloneResourceBaseUpdateInput, err = model.SStandaloneResourceBase.ValidateUpdateData(ctx, userCred, query, input.StandaloneResourceBaseUpdateInput) + if err != nil { + return input, errors.Wrap(err, "SStandaloneResourceBase.ValidateUpdateData") + } + return input, nil } +func (model *SEnabledIdentityBaseResource) ValidateUpdateData( + ctx context.Context, + userCred mcclient.TokenCredential, + query jsonutils.JSONObject, + input api.EnabledIdentityBaseUpdateInput, +) (api.EnabledIdentityBaseUpdateInput, error) { + var err error + input.IdentityBaseUpdateInput, err = model.SIdentityBaseResource.ValidateUpdateData(ctx, userCred, query, input.IdentityBaseUpdateInput) + if err != nil { + return input, errors.Wrap(err, "SIdentityBaseResource.ValidateUpdateData") + } + return input, nil +} + +/*func(manager *SIdentityBaseResourceManager) NamespaceScope() rbacutils.TRbacScope { + return rbacutils.ScopeSystem +}*/ + func (model *SIdentityBaseResource) GetExtraDetails( ctx context.Context, userCred mcclient.TokenCredential, diff --git a/pkg/keystone/models/policies.go b/pkg/keystone/models/policies.go index 68802c69e8..ffaec295b6 100644 --- a/pkg/keystone/models/policies.go +++ b/pkg/keystone/models/policies.go @@ -18,6 +18,8 @@ import ( "context" "database/sql" + "yunion.io/x/onecloud/pkg/apis" + "yunion.io/x/jsonutils" "yunion.io/x/pkg/errors" "yunion.io/x/sqlchemy" @@ -131,31 +133,26 @@ func (manager *SPolicyManager) ValidateCreateData(ctx context.Context, userCred return data, nil } -func (policy *SPolicy) ValidateUpdateData(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, data *jsonutils.JSONDict) (*jsonutils.JSONDict, error) { - if data.Contains("blob") { - blobJson, err := data.Get("blob") - if err != nil { - return nil, httperrors.NewInputParameterError("invalid policy data") - } +func (policy *SPolicy) ValidateUpdateData(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, input api.PolicyUpdateInput) (api.PolicyUpdateInput, error) { + if input.Blob != nil { p := rbacutils.SRbacPolicy{} - err = p.Decode(blobJson) + err := p.Decode(input.Blob) if err != nil { - return nil, httperrors.NewInputParameterError("fail to decode policy data") + return input, httperrors.NewInputParameterError("fail to decode policy data") } /* if p.IsSystemWidePolicy() && policyman.PolicyManager.Allow(rbacutils.ScopeSystem, userCred, consts.GetServiceType(), policy.GetModelManager().KeywordPlural(), policyman.PolicyActionUpdate) == rbacutils.Deny { return nil, httperrors.NewNotSufficientPrivilegeError("not allow to update system-wide policy") } */ } - if data.Contains("type") { - typeStr, _ := data.GetString("type") - if len(typeStr) == 0 { - return nil, httperrors.NewInputParameterError("empty name") - } - if len(typeStr) > 0 { - data.Set("name", jsonutils.NewString(typeStr)) - } + if len(input.Type) > 0 { + input.Name = input.Type } - return policy.SEnabledIdentityBaseResource.ValidateUpdateData(ctx, userCred, query, data) + var err error + input.EnabledIdentityBaseUpdateInput, err = policy.SEnabledIdentityBaseResource.ValidateUpdateData(ctx, userCred, query, input.EnabledIdentityBaseUpdateInput) + if err != nil { + return input, errors.Wrap(err, "SEnabledIdentityBaseResource.ValidateUpdateData") + } + return input, nil } func (policy *SPolicy) PostCreate(ctx context.Context, userCred mcclient.TokenCredential, ownerId mcclient.IIdentityProvider, query jsonutils.JSONObject, data jsonutils.JSONObject) { @@ -173,28 +170,38 @@ func (policy *SPolicy) PostDelete(ctx context.Context, userCred mcclient.TokenCr policyman.PolicyManager.SyncOnce() } -func (policy *SPolicy) AllowPerformPublic(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, data jsonutils.JSONObject) bool { - return db.SharableAllowPerformPublic(policy, userCred) +func (policy *SPolicy) IsSharable(reqUsrId mcclient.IIdentityProvider) bool { + return db.SharableModelIsSharable(policy, reqUsrId) } -func (policy *SPolicy) PerformPublic(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, data jsonutils.JSONObject) (jsonutils.JSONObject, error) { - res, err := db.SharablePerformPublic(policy, ctx, userCred, query, data) - if err == nil { - policyman.PolicyManager.SyncOnce() +func (policy *SPolicy) IsShared() bool { + return db.SharableModelIsShared(policy) +} + +func (policy *SPolicy) AllowPerformPublic(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, input apis.PerformPublicInput) bool { + return true +} + +func (policy *SPolicy) PerformPublic(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, input apis.PerformPublicInput) (jsonutils.JSONObject, error) { + err := db.SharablePerformPublic(policy, ctx, userCred, input) + if err != nil { + return nil, errors.Wrap(err, "SharablePerformPublic") } - return res, err + policyman.PolicyManager.SyncOnce() + return nil, nil } -func (policy *SPolicy) AllowPerformPrivate(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, data jsonutils.JSONObject) bool { - return db.SharableAllowPerformPrivate(policy, userCred) +func (policy *SPolicy) AllowPerformPrivate(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, input apis.PerformPrivateInput) bool { + return true } -func (policy *SPolicy) PerformPrivate(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, data jsonutils.JSONObject) (jsonutils.JSONObject, error) { - res, err := db.SharablePerformPrivate(policy, ctx, userCred, query, data) - if err == nil { - policyman.PolicyManager.SyncOnce() +func (policy *SPolicy) PerformPrivate(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, input apis.PerformPrivateInput) (jsonutils.JSONObject, error) { + err := db.SharablePerformPrivate(policy, ctx, userCred) + if err != nil { + return nil, errors.Wrap(err, "SharablePerformPrivate") } - return res, err + policyman.PolicyManager.SyncOnce() + return nil, nil } func (policy *SPolicy) ValidateDeleteCondition(ctx context.Context) error { diff --git a/pkg/keystone/models/projects.go b/pkg/keystone/models/projects.go index b200713a79..53fcd7feca 100644 --- a/pkg/keystone/models/projects.go +++ b/pkg/keystone/models/projects.go @@ -315,13 +315,19 @@ func (proj *SProject) IsAdminProject() bool { return proj.Name == api.SystemAdminProject && proj.DomainId == api.DEFAULT_DOMAIN_ID } -func (proj *SProject) ValidateUpdateData(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, data *jsonutils.JSONDict) (*jsonutils.JSONDict, error) { - if data.Contains("name") { +func (proj *SProject) ValidateUpdateData(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, input api.ProjectUpdateInput) (api.ProjectUpdateInput, error) { + if len(input.Name) > 0 { if proj.IsAdminProject() { - return nil, httperrors.NewForbiddenError("cannot alter system project name") + return input, httperrors.NewForbiddenError("cannot alter system project name") } } - return proj.SIdentityBaseResource.ValidateUpdateData(ctx, userCred, query, data) + var err error + input.IdentityBaseUpdateInput, err = proj.SIdentityBaseResource.ValidateUpdateData(ctx, userCred, query, input.IdentityBaseUpdateInput) + if err != nil { + return input, errors.Wrap(err, "SIdentityBaseResource.ValidateUpdateData") + } + + return input, nil } func (manager *SProjectManager) FetchCustomizeColumns( diff --git a/pkg/keystone/models/roles.go b/pkg/keystone/models/roles.go index b8f1bd1e41..07cb9047f9 100644 --- a/pkg/keystone/models/roles.go +++ b/pkg/keystone/models/roles.go @@ -19,6 +19,8 @@ import ( "database/sql" "fmt" + "yunion.io/x/onecloud/pkg/apis" + "yunion.io/x/jsonutils" "yunion.io/x/log" "yunion.io/x/pkg/errors" @@ -64,7 +66,7 @@ func init() { */ type SRole struct { - SIdentityBaseResource + SIdentityBaseResource `"name->update":"none"` db.SSharableBaseResource } @@ -167,11 +169,17 @@ func (role *SRole) GetProjectCount() (int, error) { return q.CountWithError() } -func (role *SRole) ValidateUpdateData(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, data *jsonutils.JSONDict) (*jsonutils.JSONDict, error) { - if data.Contains("name") { - return nil, httperrors.NewForbiddenError("cannot alter name of role") +func (role *SRole) ValidateUpdateData(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, input api.RoleUpdateInput) (api.RoleUpdateInput, error) { + if len(input.Name) > 0 { + return input, httperrors.NewForbiddenError("cannot alter name of role") } - return role.SIdentityBaseResource.ValidateUpdateData(ctx, userCred, query, data) + var err error + input.IdentityBaseUpdateInput, err = role.SIdentityBaseResource.ValidateUpdateData(ctx, userCred, query, input.IdentityBaseUpdateInput) + if err != nil { + return input, errors.Wrap(err, "SIdentityBaseResource.ValidateUpdateData") + } + + return input, nil } func (role *SRole) IsSystemRole() bool { @@ -400,28 +408,38 @@ func (manager *SRoleManager) FetchRole(roleId, roleName string, domainId, domain return nil, fmt.Errorf("no role Id or name provided") } -func (role *SRole) AllowPerformPublic(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, data jsonutils.JSONObject) bool { - return db.SharableAllowPerformPublic(role, userCred) +func (role *SRole) IsShared() bool { + return db.SharableModelIsShared(role) } -func (role *SRole) PerformPublic(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, data jsonutils.JSONObject) (jsonutils.JSONObject, error) { - res, err := db.SharablePerformPublic(role, ctx, userCred, query, data) - if err == nil { - policy.PolicyManager.SyncOnce() +func (role *SRole) IsSharable(reqUsrId mcclient.IIdentityProvider) bool { + return db.SharableModelIsSharable(role, reqUsrId) +} + +func (role *SRole) AllowPerformPublic(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, input apis.PerformPublicInput) bool { + return true +} + +func (role *SRole) PerformPublic(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, input apis.PerformPublicInput) (jsonutils.JSONObject, error) { + err := db.SharablePerformPublic(role, ctx, userCred, input) + if err != nil { + return nil, errors.Wrap(err, "SharablePerformPublic") } - return res, err + policy.PolicyManager.SyncOnce() + return nil, nil } -func (role *SRole) AllowPerformPrivate(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, data jsonutils.JSONObject) bool { - return db.SharableAllowPerformPrivate(role, userCred) +func (role *SRole) AllowPerformPrivate(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, input apis.PerformPrivateInput) bool { + return true } -func (role *SRole) PerformPrivate(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, data jsonutils.JSONObject) (jsonutils.JSONObject, error) { - res, err := db.SharablePerformPrivate(role, ctx, userCred, query, data) - if err == nil { - policy.PolicyManager.SyncOnce() +func (role *SRole) PerformPrivate(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, input apis.PerformPrivateInput) (jsonutils.JSONObject, error) { + err := db.SharablePerformPrivate(role, ctx, userCred) + if err != nil { + return nil, errors.Wrap(err, "SharablePerformPrivate") } - return res, err + policy.PolicyManager.SyncOnce() + return nil, nil } func (manager *SRoleManager) ValidateCreateData(ctx context.Context, userCred mcclient.TokenCredential, ownerId mcclient.IIdentityProvider, query jsonutils.JSONObject, data *jsonutils.JSONDict) (*jsonutils.JSONDict, error) { diff --git a/pkg/keystone/models/users.go b/pkg/keystone/models/users.go index 3b61fb96d3..36233029e2 100644 --- a/pkg/keystone/models/users.go +++ b/pkg/keystone/models/users.go @@ -466,13 +466,14 @@ func (manager *SUserManager) ValidateCreateData(ctx context.Context, userCred mc return data, nil } -func (user *SUser) ValidateUpdateData(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, data *jsonutils.JSONDict) (*jsonutils.JSONDict, error) { - if data.Contains("name") { +func (user *SUser) ValidateUpdateData(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, input api.UserUpdateInput) (api.UserUpdateInput, error) { + if len(input.Name) > 0 { if user.IsAdminUser() { - return nil, httperrors.NewForbiddenError("cannot alter sysadmin user name") + return input, httperrors.NewForbiddenError("cannot alter sysadmin user name") } } if user.IsReadOnly() { + data := jsonutils.Marshal(input) for _, k := range []string{ "name", "enabled", @@ -482,15 +483,15 @@ func (user *SUser) ValidateUpdateData(ctx context.Context, userCred mcclient.Tok "password", } { if data.Contains(k) { - return nil, httperrors.NewForbiddenError("field %s is readonly", k) + return input, httperrors.NewForbiddenError("field %s is readonly", k) } } } - if data.Contains("password") { - passwd, _ := data.GetString("password") + if len(input.Password) > 0 { + passwd := input.Password usrExt, err := UserManager.FetchUserExtended(user.Id, "", "", "") if err != nil { - return nil, errors.Wrap(err, "UserManager.FetchUserExtended") + return input, errors.Wrap(err, "UserManager.FetchUserExtended") } skipHistoryCheck := false if user.IsSystemAccount.Bool() { @@ -498,10 +499,16 @@ func (user *SUser) ValidateUpdateData(ctx context.Context, userCred mcclient.Tok } err = PasswordManager.validatePassword(usrExt.LocalId, passwd, skipHistoryCheck) if err != nil { - return nil, httperrors.NewInputParameterError("invalid password: %s", err) + return input, httperrors.NewInputParameterError("invalid password: %s", err) } } - return user.SEnabledIdentityBaseResource.ValidateUpdateData(ctx, userCred, query, data) + var err error + input.EnabledIdentityBaseUpdateInput, err = user.SEnabledIdentityBaseResource.ValidateUpdateData(ctx, userCred, query, input.EnabledIdentityBaseUpdateInput) + if err != nil { + return input, errors.Wrap(err, "SEnabledIdentityBaseResource.ValidateUpdateData") + } + + return input, nil } func (user *SUser) ValidateUpdateCondition(ctx context.Context) error { diff --git a/pkg/mcclient/modules/mod_bucket_options.go b/pkg/mcclient/modules/mod_bucket_options.go index 4746e90e10..3abb252581 100644 --- a/pkg/mcclient/modules/mod_bucket_options.go +++ b/pkg/mcclient/modules/mod_bucket_options.go @@ -1,3 +1,17 @@ +// 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 modules import "yunion.io/x/onecloud/pkg/mcclient/modulebase" diff --git a/pkg/mcclient/modules/mod_hosts.go b/pkg/mcclient/modules/mod_hosts.go index e217767ea4..3a132f6435 100644 --- a/pkg/mcclient/modules/mod_hosts.go +++ b/pkg/mcclient/modules/mod_hosts.go @@ -184,6 +184,7 @@ func init() { "host_type", "version", "schedtags", "storage_size", "expired_at", + "domain_id", "project_domain", }, []string{})} registerCompute(&Hosts) diff --git a/pkg/mcclient/modules/monitor/doc.go b/pkg/mcclient/modules/monitor/doc.go index b06f5a99d1..3f8597d73f 100644 --- a/pkg/mcclient/modules/monitor/doc.go +++ b/pkg/mcclient/modules/monitor/doc.go @@ -1 +1,15 @@ +// 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 monitor // import "yunion.io/x/onecloud/pkg/mcclient/modules/monitor" diff --git a/pkg/mcclient/options/monitor/alert.go b/pkg/mcclient/options/monitor/alert.go index 9ca1e508cb..d0a9bde349 100644 --- a/pkg/mcclient/options/monitor/alert.go +++ b/pkg/mcclient/options/monitor/alert.go @@ -43,7 +43,7 @@ type AlertUpdateOptions struct { func (opt AlertUpdateOptions) Params() (*monitor.AlertUpdateInput, error) { input := new(monitor.AlertUpdateInput) if opt.Name != "" { - input.Name = &opt.Name + input.Name = opt.Name } if opt.Frequency != "" { freq, err := time.ParseDuration(opt.Frequency) diff --git a/pkg/mcclient/options/monitor/doc.go b/pkg/mcclient/options/monitor/doc.go index fc8c4a12e0..4a7d895b9c 100644 --- a/pkg/mcclient/options/monitor/doc.go +++ b/pkg/mcclient/options/monitor/doc.go @@ -1 +1,15 @@ +// 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 monitor // import "yunion.io/x/onecloud/pkg/mcclient/options/monitor" diff --git a/pkg/monitor/alerting/conditions/doc.go b/pkg/monitor/alerting/conditions/doc.go index 82544f5e02..7075d92a4f 100644 --- a/pkg/monitor/alerting/conditions/doc.go +++ b/pkg/monitor/alerting/conditions/doc.go @@ -1 +1,15 @@ +// 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 conditions // import "yunion.io/x/onecloud/pkg/monitor/alerting/conditions" diff --git a/pkg/monitor/alerting/doc.go b/pkg/monitor/alerting/doc.go index c92ecbcc01..a5ad5daff2 100644 --- a/pkg/monitor/alerting/doc.go +++ b/pkg/monitor/alerting/doc.go @@ -1 +1,15 @@ +// 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 alerting // import "yunion.io/x/onecloud/pkg/monitor/alerting" diff --git a/pkg/monitor/alerting/notifiers/doc.go b/pkg/monitor/alerting/notifiers/doc.go index aab0a84ea1..c495f0faf3 100644 --- a/pkg/monitor/alerting/notifiers/doc.go +++ b/pkg/monitor/alerting/notifiers/doc.go @@ -1 +1,15 @@ +// 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 notifiers // import "yunion.io/x/onecloud/pkg/monitor/alerting/notifiers" diff --git a/pkg/monitor/alerting/notifiers/templates/doc.go b/pkg/monitor/alerting/notifiers/templates/doc.go index cb2af4cc1b..f17da68b10 100644 --- a/pkg/monitor/alerting/notifiers/templates/doc.go +++ b/pkg/monitor/alerting/notifiers/templates/doc.go @@ -1 +1,15 @@ +// 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 templates // import "yunion.io/x/onecloud/pkg/monitor/alerting/notifiers/templates" diff --git a/pkg/monitor/bus/doc.go b/pkg/monitor/bus/doc.go index 7414593d11..94e600cefd 100644 --- a/pkg/monitor/bus/doc.go +++ b/pkg/monitor/bus/doc.go @@ -1 +1,15 @@ +// 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 bus // import "yunion.io/x/onecloud/pkg/monitor/bus" diff --git a/pkg/monitor/expressions/doc.go b/pkg/monitor/expressions/doc.go index 2a606b9cda..1b44ef4dae 100644 --- a/pkg/monitor/expressions/doc.go +++ b/pkg/monitor/expressions/doc.go @@ -1 +1,15 @@ +// 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 expressions // import "yunion.io/x/onecloud/pkg/monitor/expressions" diff --git a/pkg/monitor/models/alert.go b/pkg/monitor/models/alert.go index f83c12638e..1f0552993c 100644 --- a/pkg/monitor/models/alert.go +++ b/pkg/monitor/models/alert.go @@ -369,17 +369,23 @@ func (alert *SAlert) SetState(input AlertSetStateInput) error { return err } -func (alert *SAlert) ValidateUpdateData(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, input monitor.AlertUpdateInput) (*jsonutils.JSONDict, error) { +func (alert *SAlert) ValidateUpdateData(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, input monitor.AlertUpdateInput) (monitor.AlertUpdateInput, error) { if input.Settings != nil { updateSettings := jsonutils.NewDict() updateSettings.Update(alert.Settings) updateSettings.Update(jsonutils.Marshal(input.Settings)) input.Settings = new(monitor.AlertSetting) if err := updateSettings.Unmarshal(input.Settings); err != nil { - return nil, err + return input, err } } - return alert.SVirtualResourceBase.ValidateUpdateData(ctx, userCred, query, input.JSON(input)) + var err error + input.VirtualResourceBaseUpdateInput, err = alert.SVirtualResourceBase.ValidateUpdateData(ctx, userCred, query, input.VirtualResourceBaseUpdateInput) + if err != nil { + return input, errors.Wrap(err, "SVirtualResourceBase.ValidateUpdateData") + } + + return input, nil } func (alert *SAlert) IsAttachNotification(noti *SNotification) (bool, error) { diff --git a/pkg/monitor/models/doc.go b/pkg/monitor/models/doc.go index 71c24a40de..5181c0dd5e 100644 --- a/pkg/monitor/models/doc.go +++ b/pkg/monitor/models/doc.go @@ -1 +1,15 @@ +// 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 "yunion.io/x/onecloud/pkg/monitor/models" diff --git a/pkg/monitor/models/meteralert.go b/pkg/monitor/models/meteralert.go index a6a1e06cfa..2dbf04db7c 100644 --- a/pkg/monitor/models/meteralert.go +++ b/pkg/monitor/models/meteralert.go @@ -575,12 +575,15 @@ func (alert *SMeterAlert) getMoreDetails(out monitor.MeterAlertDetails) (monitor } func (alert *SMeterAlert) ValidateUpdateData( - ctx context.Context, userCred mcclient.TokenCredential, - query jsonutils.JSONObject, input monitor.MeterAlertUpdateInput) (*jsonutils.JSONDict, error) { - ret := new(monitor.AlertUpdateInput) + ctx context.Context, + userCred mcclient.TokenCredential, + query jsonutils.JSONObject, + input monitor.MeterAlertUpdateInput, +) (monitor.MeterAlertUpdateInput, error) { + // ret := new(monitor.AlertUpdateInput) details, err := alert.getExtraDetails(ctx, userCred, query, false) if err != nil { - return nil, err + return input, err } if input.Threshold != nil && *input.Threshold != details.Threshold { details.Threshold = *input.Threshold @@ -592,22 +595,29 @@ func (alert *SMeterAlert) ValidateUpdateData( ds, err := DataSourceManager.GetDefaultSource() if err != nil { - return nil, errors.Wrap(err, "get default data source") + return input, errors.Wrap(err, "get default data source") } + // hack: update notification here if err := alert.UpdateNotification(AlertNotificationUsedByMeterAlert, input.Channel, input.Recipients); err != nil { - return nil, errors.Wrap(err, "update notification") + return input, errors.Wrap(err, "update notification") } allAccountIds := []string{} if details.AccountId == "" { allAccountIds, err = MeterAlertManager.getAllBillAccountIds(ctx) if err != nil { - return nil, err + return input, err } } tmpS := alert.getUpdateSetting(details, ds.GetId(), allAccountIds) - ret.Settings = &tmpS - return alert.SAlert.ValidateUpdateData(ctx, userCred, query, *ret) + input.Settings = &tmpS + + input.V1AlertUpdateInput, err = alert.SV1Alert.ValidateUpdateData(ctx, userCred, query, input.V1AlertUpdateInput) + if err != nil { + return input, errors.Wrap(err, "SV1Alert.ValidateUpdateData") + } + + return input, nil } func (alert *SMeterAlert) getUpdateSetting(details monitor.MeterAlertDetails, dsId string, accountIds []string) monitor.AlertSetting { diff --git a/pkg/monitor/models/nodealert.go b/pkg/monitor/models/nodealert.go index 6941e7f362..2dc344ba71 100644 --- a/pkg/monitor/models/nodealert.go +++ b/pkg/monitor/models/nodealert.go @@ -226,6 +226,20 @@ func (man *SV1AlertManager) QueryDistinctExtraField(q *sqlchemy.SQuery, field st return q, httperrors.ErrNotFound } +func (alertV1 *SV1Alert) ValidateUpdateData( + ctx context.Context, + userCred mcclient.TokenCredential, + query jsonutils.JSONObject, + input monitor.V1AlertUpdateInput, +) (monitor.V1AlertUpdateInput, error) { + var err error + input.AlertUpdateInput, err = alertV1.SAlert.ValidateUpdateData(ctx, userCred, query, input.AlertUpdateInput) + if err != nil { + return input, errors.Wrap(err, "SAlert.ValidateUpdateData") + } + return input, nil +} + func (man *SNodeAlertManager) ListItemFilter( ctx context.Context, q *sqlchemy.SQuery, userCred mcclient.TokenCredential, @@ -691,53 +705,57 @@ func (alert *SV1Alert) CustomizeDelete( } func (alert *SNodeAlert) ValidateUpdateData( - ctx context.Context, userCred mcclient.TokenCredential, - query jsonutils.JSONObject, input monitor.NodeAlertUpdateInput) (*jsonutils.JSONDict, error) { - ret := monitor.AlertUpdateInput{} - details, err := alert.GetExtraDetails(context.TODO(), nil, nil, false) - if err != nil { - return nil, err + ctx context.Context, + userCred mcclient.TokenCredential, + query jsonutils.JSONObject, + input monitor.NodeAlertUpdateInput, +) (monitor.NodeAlertUpdateInput, error) { + // ret := monitor.AlertUpdateInput{} + detailsList := NodeAlertManager.FetchCustomizeColumns(ctx, userCred, query, []interface{}{alert}, nil, false) + if len(detailsList) == 0 { + panic("inconsistent return results of FetchCustomizeColumns") } + details := detailsList[0] nameChange := false if input.NodeId != nil && *input.NodeId != details.NodeId { nameChange = true details.NodeId = *input.NodeId if err := alert.setNodeId(ctx, userCred, details.NodeId); err != nil { - return nil, err + return input, err } } if input.Type != nil && *input.Type != details.Type { nameChange = true details.Type = *input.Type if err := alert.setType(ctx, userCred, details.Type); err != nil { - return nil, err + return input, err } } nodeName, resType, err := NodeAlertManager.validateResourceId(ctx, details.Type, details.NodeId) if err != nil { - return nil, err + return input, err } if details.NodeName != nodeName { nameChange = true if err := alert.setNodeName(ctx, userCred, nodeName); err != nil { - return nil, err + return input, err } details.NodeName = nodeName } if input.Level != nil && *input.Level != details.Level { details.Level = *input.Level - ret.Level = input.Level + // ret.Level = input.Level } if input.Window != nil && *input.Window != details.Window { details.Window = *input.Window freq, err := time.ParseDuration(details.Window) if err != nil { - return nil, err + return input, err } freqSec := int64(freq / time.Second) - ret.Frequency = &freqSec + input.Frequency = &freqSec } if input.Threshold != nil && *input.Threshold != details.Threshold { @@ -756,7 +774,7 @@ func (alert *SNodeAlert) ValidateUpdateData( details.Metric = *input.Metric measurement, field, err := GetMeasurementField(*input.Metric) if err != nil { - return nil, err + return input, err } details.Measurement = measurement details.Field = field @@ -766,22 +784,28 @@ func (alert *SNodeAlert) ValidateUpdateData( if nameChange { name, err = NodeAlertManager.genName(userCred, resType, details.NodeName, details.Metric) if err != nil { - return nil, err + return input, err } - ret.Name = &name + input.Name = name } ds, err := DataSourceManager.GetDefaultSource() if err != nil { - return nil, errors.Wrap(err, "get default data source") + return input, errors.Wrap(err, "get default data source") } // hack: update notification here if err := alert.UpdateNotification(AlertNotificationUsedByNodeAlert, input.Channel, input.Recipients); err != nil { - return nil, errors.Wrap(err, "update notification") + return input, errors.Wrap(err, "update notification") } tmpS := alert.getUpdateSetting(name, details, ds.GetId()) - ret.Settings = &tmpS - return alert.SAlert.ValidateUpdateData(ctx, userCred, query, ret) + input.Settings = &tmpS + + input.V1AlertUpdateInput, err = alert.SV1Alert.ValidateUpdateData(ctx, userCred, query, input.V1AlertUpdateInput) + if err != nil { + return input, errors.Wrap(err, "SV1Alert.ValidateUpdateData") + } + + return input, nil } func (alert *SNodeAlert) getUpdateSetting( diff --git a/pkg/monitor/notifydrivers/doc.go b/pkg/monitor/notifydrivers/doc.go index 4f93eab572..bf18db812e 100644 --- a/pkg/monitor/notifydrivers/doc.go +++ b/pkg/monitor/notifydrivers/doc.go @@ -1 +1,15 @@ +// 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 notifydrivers // import "yunion.io/x/onecloud/pkg/monitor/notifydrivers" diff --git a/pkg/monitor/notifydrivers/feishu/doc.go b/pkg/monitor/notifydrivers/feishu/doc.go index 2607ffac10..c1789e3d3d 100644 --- a/pkg/monitor/notifydrivers/feishu/doc.go +++ b/pkg/monitor/notifydrivers/feishu/doc.go @@ -1 +1,15 @@ +// 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 feishu // import "yunion.io/x/onecloud/pkg/monitor/notifydrivers/feishu" diff --git a/pkg/monitor/options/doc.go b/pkg/monitor/options/doc.go index 3605a59b40..aa74abcc8c 100644 --- a/pkg/monitor/options/doc.go +++ b/pkg/monitor/options/doc.go @@ -1 +1,15 @@ +// 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 options // import "yunion.io/x/onecloud/pkg/monitor/options" diff --git a/pkg/monitor/registry/doc.go b/pkg/monitor/registry/doc.go index 577ef894bd..652fc4908c 100644 --- a/pkg/monitor/registry/doc.go +++ b/pkg/monitor/registry/doc.go @@ -1 +1,15 @@ +// 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 registry // import "yunion.io/x/onecloud/pkg/monitor/registry" diff --git a/pkg/monitor/service/doc.go b/pkg/monitor/service/doc.go index 3b3463a554..d263fef47a 100644 --- a/pkg/monitor/service/doc.go +++ b/pkg/monitor/service/doc.go @@ -1 +1,15 @@ +// 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 service // import "yunion.io/x/onecloud/pkg/monitor/service" diff --git a/pkg/monitor/tsdb/doc.go b/pkg/monitor/tsdb/doc.go index c60baf872e..b3044ba86f 100644 --- a/pkg/monitor/tsdb/doc.go +++ b/pkg/monitor/tsdb/doc.go @@ -1 +1,15 @@ +// 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 tsdb // import "yunion.io/x/onecloud/pkg/monitor/tsdb" diff --git a/pkg/monitor/tsdb/driver/influxdb/doc.go b/pkg/monitor/tsdb/driver/influxdb/doc.go index 6193d147f9..510bc37188 100644 --- a/pkg/monitor/tsdb/driver/influxdb/doc.go +++ b/pkg/monitor/tsdb/driver/influxdb/doc.go @@ -1 +1,15 @@ +// 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 influxdb // import "yunion.io/x/onecloud/pkg/monitor/tsdb/driver/influxdb" diff --git a/pkg/monitor/validators/doc.go b/pkg/monitor/validators/doc.go index a78c3c496c..4ea1bafb63 100644 --- a/pkg/monitor/validators/doc.go +++ b/pkg/monitor/validators/doc.go @@ -1 +1,15 @@ +// 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 validators // import "yunion.io/x/onecloud/pkg/monitor/validators" diff --git a/pkg/multicloud/google/monitor.go b/pkg/multicloud/google/monitor.go index ca98ba71be..1b3084f5f3 100644 --- a/pkg/multicloud/google/monitor.go +++ b/pkg/multicloud/google/monitor.go @@ -1,3 +1,17 @@ +// 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 google import ( diff --git a/pkg/multicloud/google/s3object.go b/pkg/multicloud/google/s3object.go index 46505eeed7..d045ba3f41 100644 --- a/pkg/multicloud/google/s3object.go +++ b/pkg/multicloud/google/s3object.go @@ -1,3 +1,17 @@ +// 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. + // Copyright 2019 Yunion // diff --git a/pkg/util/fileutils2/writable_others.go b/pkg/util/fileutils2/writable_others.go index 4ffa109724..af61eaaa6d 100644 --- a/pkg/util/fileutils2/writable_others.go +++ b/pkg/util/fileutils2/writable_others.go @@ -1,3 +1,17 @@ +// 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. + // +build !windows package fileutils2 diff --git a/pkg/util/fileutils2/writable_windows.go b/pkg/util/fileutils2/writable_windows.go index bfc36f60b2..b700430fe8 100644 --- a/pkg/util/fileutils2/writable_windows.go +++ b/pkg/util/fileutils2/writable_windows.go @@ -1,3 +1,17 @@ +// 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 fileutils2 func Writable(path string) bool { diff --git a/pkg/util/k8s/tokens/doc.go b/pkg/util/k8s/tokens/doc.go index 8a2647d78a..ca56cff644 100644 --- a/pkg/util/k8s/tokens/doc.go +++ b/pkg/util/k8s/tokens/doc.go @@ -1 +1,15 @@ +// 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 tokens // import "yunion.io/x/onecloud/pkg/util/k8s/tokens" diff --git a/pkg/util/k8s/tokens/tokens.go b/pkg/util/k8s/tokens/tokens.go index 13f4dea49b..9292d2ef98 100644 --- a/pkg/util/k8s/tokens/tokens.go +++ b/pkg/util/k8s/tokens/tokens.go @@ -1,3 +1,17 @@ +// 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 tokens import ( diff --git a/pkg/util/procutils/cmdsetsid_others.go b/pkg/util/procutils/cmdsetsid_others.go index 583186e11d..32197eb6ce 100644 --- a/pkg/util/procutils/cmdsetsid_others.go +++ b/pkg/util/procutils/cmdsetsid_others.go @@ -1,3 +1,17 @@ +// 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. + // +build !windows package procutils diff --git a/pkg/util/procutils/cmdsetsid_windows.go b/pkg/util/procutils/cmdsetsid_windows.go index dfbd19de89..e8d05e25e4 100644 --- a/pkg/util/procutils/cmdsetsid_windows.go +++ b/pkg/util/procutils/cmdsetsid_windows.go @@ -1,3 +1,17 @@ +// 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 procutils import (