diff --git a/api/src/main/java/org/apache/cloudstack/dns/DnsServer.java b/api/src/main/java/org/apache/cloudstack/dns/DnsServer.java index 0261902ef98..4e08ba1a937 100644 --- a/api/src/main/java/org/apache/cloudstack/dns/DnsServer.java +++ b/api/src/main/java/org/apache/cloudstack/dns/DnsServer.java @@ -41,7 +41,7 @@ public interface DnsServer extends InternalIdentity, Identity, ControlledEntity long getAccountId(); - boolean isPublicServer(); + boolean getPublicServer(); Date getCreated(); diff --git a/server/src/main/java/com/cloud/user/AccountManagerImpl.java b/server/src/main/java/com/cloud/user/AccountManagerImpl.java index 2f6b64262b8..76de214bc4d 100644 --- a/server/src/main/java/com/cloud/user/AccountManagerImpl.java +++ b/server/src/main/java/com/cloud/user/AccountManagerImpl.java @@ -3640,7 +3640,7 @@ public class AccountManagerImpl extends ManagerBase implements AccountManager, M if (caller.getId() == dnsServer.getAccountId()) { return; } - if (!dnsServer.isPublicServer()) { + if (!dnsServer.getPublicServer()) { throw new PermissionDeniedException(caller + "is not allowed to access the DNS server " + dnsServer.getName()); } Account owner = getAccount(dnsServer.getAccountId()); diff --git a/server/src/main/java/org/apache/cloudstack/dns/DnsProviderManagerImpl.java b/server/src/main/java/org/apache/cloudstack/dns/DnsProviderManagerImpl.java index 9c8bfe4ba30..811a8499b18 100644 --- a/server/src/main/java/org/apache/cloudstack/dns/DnsProviderManagerImpl.java +++ b/server/src/main/java/org/apache/cloudstack/dns/DnsProviderManagerImpl.java @@ -230,7 +230,7 @@ public class DnsProviderManagerImpl extends ManagerBase implements DnsProviderMa dnsServer.setPort(cmd.getPort()); } if (cmd.isPublic() != null) { - dnsServer.setIsPublic(cmd.isPublic()); + dnsServer.setPublicServer(cmd.isPublic()); } if (cmd.getPublicDomainSuffix() != null) { @@ -476,7 +476,7 @@ public class DnsProviderManagerImpl extends ManagerBase implements DnsProviderMa Account caller = CallContext.current().getCallingAccount(); boolean isOwner = (server.getAccountId() == caller.getId()); if (!isOwner) { - if (!server.isPublicServer()) { + if (!server.getPublicServer()) { throw new PermissionDeniedException("You do not have permission to use this DNS server."); } dnsZoneName = DnsProviderUtil.appendPublicSuffixToZone(dnsZoneName, server.getPublicDomainSuffix()); diff --git a/server/src/main/java/org/apache/cloudstack/dns/dao/DnsServerDaoImpl.java b/server/src/main/java/org/apache/cloudstack/dns/dao/DnsServerDaoImpl.java index 095b4eb640c..8eff7397c8f 100644 --- a/server/src/main/java/org/apache/cloudstack/dns/dao/DnsServerDaoImpl.java +++ b/server/src/main/java/org/apache/cloudstack/dns/dao/DnsServerDaoImpl.java @@ -110,7 +110,7 @@ public class DnsServerDaoImpl extends GenericDaoBase implemen sb.and().op(ApiConstants.ACCOUNT_ID, sb.entity().getAccountId(), SearchCriteria.Op.EQ); if (!CollectionUtils.isEmpty(domainIds)) { - sb.or().op(ApiConstants.IS_PUBLIC, sb.entity().isPublicServer(), SearchCriteria.Op.EQ); + sb.or().op(ApiConstants.IS_PUBLIC, sb.entity().getPublicServer(), SearchCriteria.Op.EQ); sb.and(ApiConstants.DOMAIN_IDS, sb.entity().getDomainId(), SearchCriteria.Op.IN); sb.cp(); } diff --git a/server/src/main/java/org/apache/cloudstack/dns/vo/DnsServerVO.java b/server/src/main/java/org/apache/cloudstack/dns/vo/DnsServerVO.java index 5ccc5bf91f9..4db846413e9 100644 --- a/server/src/main/java/org/apache/cloudstack/dns/vo/DnsServerVO.java +++ b/server/src/main/java/org/apache/cloudstack/dns/vo/DnsServerVO.java @@ -36,7 +36,6 @@ import javax.persistence.TemporalType; import org.apache.cloudstack.dns.DnsProviderType; import org.apache.cloudstack.dns.DnsServer; -import org.apache.cloudstack.dns.DnsZone; import com.cloud.utils.StringUtils; import com.cloud.utils.db.Encrypt; @@ -123,7 +122,7 @@ public class DnsServerVO implements DnsServer { this.publicDomainSuffix = publicDomainSuffix; this.publicServer = isPublic; this.state = State.Enabled; - this.nameServers = String.join(",", nameServers);; + this.nameServers = String.join(",", nameServers); } @Override @@ -133,7 +132,7 @@ public class DnsServerVO implements DnsServer { @Override public Class getEntityType() { - return DnsZone.class; + return DnsServer.class; } @Override @@ -176,7 +175,7 @@ public class DnsServerVO implements DnsServer { return uuid; } - public boolean isPublicServer() { + public boolean getPublicServer() { return publicServer; } @@ -202,8 +201,8 @@ public class DnsServerVO implements DnsServer { this.nameServers = nameServers; } - public void setIsPublic(boolean value) { - publicServer = value; + public void setPublicServer(boolean value) { + this.publicServer = value; } public void setPublicDomainSuffix(String publicDomainSuffix) { diff --git a/ui/public/locales/en.json b/ui/public/locales/en.json index e82eb80e7d9..bc8b35c8505 100644 --- a/ui/public/locales/en.json +++ b/ui/public/locales/en.json @@ -292,11 +292,6 @@ "label.add.internal.lb": "Add internal LB", "label.add.ip.range": "Add IP Range", "label.add.ipv4.subnet": "Add IPv4 Subnet for Routed Networks", -"label.dns.server": "DNS Server", -"label.dns.add.server": "Add DNS Server", -"label.dns.update.server": "Update DNS Server", -"label.dns.delete.server": "Delete DNS Server", -"label.dnsrecords": "DNS Records", "label.add.ip.v6.prefix": "Add IPv6 prefix", "label.add.isolated.network": "Add Isolated Network", "label.add.kubernetes.cluster": "Add Kubernetes Cluster", @@ -654,6 +649,7 @@ "label.console.proxy": "Console proxy", "label.console.proxy.vm": "Console proxy VM", "label.contains": "Contains", +"label.contents": "Contents", "label.continue": "Continue", "label.continue.install": "Continue with installation", "label.controlnodes": "Control nodes", @@ -718,7 +714,6 @@ "label.created": "Created", "label.creating": "Creating", "label.creating.iprange": "Creating IP ranges", -"label.dns.credentials": "DNS API key", "label.nameservers": "DNS Nameservers", "label.credit": "Credit", "label.cron": "Cron expression", @@ -943,9 +938,26 @@ "label.dns": "DNS", "label.dns1": "DNS 1", "label.dns2": "DNS 2", +"label.dns.add.record": "Add DNS Record", +"label.dns.add.server": "Add DNS Server", +"label.dns.add.zone": "Add DNS Zone", +"label.dns.credentials": "DNS API key", +"label.dns.delete.server": "Delete DNS Server", +"label.dns.delete.zone": "Delete DNS Zone", "label.dns.records": "DNS Records", +"label.dns.record.name.tooltip": "The hostname or subdomain for this record (e.g. www)", +"label.dns.record.type.tooltip": "The DNS record type", +"label.dns.record.contents.tooltip": "The content values for this DNS record (type and press Enter to add)", +"label.dns.record.ttl.tooltip": "Time to live in seconds", +"label.dns.record.url": "Instance URL", +"label.dns.server": "DNS Server", +"label.dnsservername": "DNS Server name", +"label.dns.servers": "DNS Servers", "label.dns.zone": "DNS Zone", "label.dns.zones": "DNS Zones", +"label.dns.update.server": "Update DNS Server", +"label.dns.update.zone": "Update DNS Zone", +"label.dnsrecords": "DNS Records", "label.domain": "Domain", "label.domain.id": "Domain ID", "label.domain.name": "Domain name", @@ -2594,6 +2606,7 @@ "label.two.factor.authentication.static.pin": "Your Two factor authentication static PIN", "label.two.factor.authentication": "Two Factor Authentication", "label.2FA": "2FA", +"label.ttl": "TTL", "label.tungsten.fabric": "Tungsten Fabric", "label.tungsten.fabric.provider": "Tungsten Fabric Provider", "label.tungsten.fabric.routing": "Tungsten Fabric Routing", @@ -2954,7 +2967,9 @@ "message.action.delete.backup.schedule": "Please confirm that you want to delete this backup schedule?", "message.action.delete.cluster": "Please confirm that you want to delete this Cluster.", "message.action.delete.custom.action": "Please confirm that you want to delete this custom action.", +"message.confirm.delete.dns.record": "Are you sure you want to delete this DNS record?", "message.action.delete.dns.server": "Please confirm you want to delete this DNS server.", +"message.action.delete.dns.zone": "Please confirm you want to delete this DNS zone.", "message.action.delete.domain": "Please confirm that you want to delete this domain.", "message.action.delete.extension": "Please confirm that you want to delete the extension", "message.action.delete.external.firewall": "Please confirm that you would like to remove this external firewall. Warning: If you are planning to add back the same external firewall, you must reset usage data on the device.", @@ -3862,6 +3877,12 @@ "message.success.add.bgp.peer": "Successfully added new BGP peer", "message.success.add.egress.rule": "Successfully added new egress rule", "message.success.add.firewall.rule": "Successfully added new firewall rule", +"message.success.add.dns.server": "Successfully added DNS server", +"message.success.add.dns.zone": "Successfully added DNS zone", +"message.success.update.dns.server": "Successfully updated DNS server", +"message.success.update.dns.zone": "Successfully updated DNS zone", +"message.success.add.dns.record": "Successfully added DNS record", +"message.success.delete.dns.record": "Successfully deleted DNS record", "message.success.add.guest.network": "Successfully created guest Network", "message.success.add.gpu.device": "Successfully added GPU device", "message.success.add.interface.static.route": "Successfully added interface Static Route", @@ -4158,6 +4179,9 @@ "migrate.from": "Migrate from", "migrate.to": "Migrate to", "migrationPolicy": "Migration policy", +"placeholder.dns.record.name": "e.g. www", +"placeholder.dns.record.type": "Select record type", +"placeholder.dns.record.contents": "Type a value and hit Enter", "placeholder.quota.tariff.activationrule": "Quota tariff's activation rule", "placeholder.quota.tariff.description": "Quota tariff's description", "placeholder.quota.tariff.enddate": "Quota tariff's end date", diff --git a/ui/src/components/view/ListView.vue b/ui/src/components/view/ListView.vue index 66dd6b3db9e..8dcc09391b9 100644 --- a/ui/src/components/view/ListView.vue +++ b/ui/src/components/view/ListView.vue @@ -1219,7 +1219,7 @@ export default { '/zone', '/pod', '/cluster', '/host', '/storagepool', '/imagestore', '/systemvm', '/router', '/ilbvm', '/annotation', '/computeoffering', '/systemoffering', '/diskoffering', '/backupoffering', '/networkoffering', '/vpcoffering', '/tungstenfabric', '/oauthsetting', '/guestos', '/guestoshypervisormapping', '/webhook', 'webhookdeliveries', 'webhookfilters', '/quotatariff', '/sharedfs', - '/ipv4subnets', '/managementserver', '/gpucard', '/gpudevices', '/vgpuprofile', '/extension', '/snapshotpolicy', '/backupschedule'].join('|')) + '/ipv4subnets', '/managementserver', '/gpucard', '/gpudevices', '/vgpuprofile', '/extension', '/snapshotpolicy', '/backupschedule', '/dnsserver', '/dnszone'].join('|')) .test(this.$route.path) }, enableGroupAction () { diff --git a/ui/src/config/section/network.js b/ui/src/config/section/network.js index 62f5ae19955..95641969fd9 100644 --- a/ui/src/config/section/network.js +++ b/ui/src/config/section/network.js @@ -1488,47 +1488,15 @@ export default { } ] }, - { - name: 'dnsrecords', - title: 'label.dns.records', - icon: 'global-outlined', - hidden: true, - permission: ['listDnsRecords'], - columns: ['name', 'url', 'provider'], - details: ['name', 'url', 'provider', 'ispublic', 'port', 'nameservers'], - related: [{ - name: 'vm', - title: 'label.dns.zone', - param: 'dnszoneid' - }] - }, - { - name: 'dnszones', - title: 'label.dns.zones', - icon: 'global-outlined', - hidden: true, - permission: ['listDnsZones'], - columns: ['name', 'state', 'dnsservername', 'dnsserveraccount'], - details: ['name', 'state', 'dnsservername', 'dnsserveraccount'], - tabs: [{ - name: 'details', - component: shallowRef(defineAsyncComponent(() => import('@/components/view/DetailsTab.vue'))) - }, - { - name: 'dnsrecords', - component: shallowRef(defineAsyncComponent(() => import('@/views/network/InternalLBAssignedVmTab.vue'))), - show: () => true - }] - }, { name: 'dnsserver', title: 'label.dns.server', icon: 'global-outlined', permission: ['listDnsServers'], columns: ['name', 'url', 'provider'], - details: ['name', 'url', 'abc', 'provider', 'ispublic', 'port', 'nameservers', 'domain', 'account'], + details: ['name', 'url', 'provider', 'ispublic', 'port', 'nameservers', 'domain', 'account'], related: [{ - name: 'dnszones', + name: 'dnszone', title: 'label.dns.zone', param: 'dnsserverid' }], @@ -1550,7 +1518,7 @@ export default { label: 'label.dns.update.server', dataView: true, popup: true, - component: shallowRef(defineAsyncComponent(() => import('@/views/network/dns/AddDnsServer.vue'))), + component: shallowRef(defineAsyncComponent(() => import('@/views/network/dns/UpdateDnsServer.vue'))), show: (record) => { return true } }, { @@ -1564,6 +1532,58 @@ export default { groupMap: (selection) => { return selection.map(x => { return { id: x } }) } } ] + }, + { + name: 'dnszone', + title: 'label.dns.zones', + icon: 'global-outlined', + permission: ['listDnsZones'], + columns: ['name', 'state', 'dnsservername', 'account'], + details: ['name', 'id', 'state', 'dnsservername', 'dnsserverid', 'account', 'domainpath'], + tabs: [{ + name: 'details', + component: shallowRef(defineAsyncComponent(() => import('@/components/view/DetailsTab.vue'))) + }, + { + name: 'dns.records', + component: shallowRef(defineAsyncComponent(() => import('@/views/network/dns/DnsRecordsTab.vue'))), + show: () => true + }], + actions: [ + { + api: 'createDnsZone', + icon: 'plus-outlined', + label: 'label.dns.add.zone', + listView: true, + popup: true, + disabled: (record) => false, + component: shallowRef(defineAsyncComponent(() => import('@/views/network/dns/AddDnsZone.vue'))), + show: () => { + return true + } + }, + { + api: 'updateDnsZone', + icon: 'edit-outlined', + label: 'label.dns.update.zone', + dataView: true, + popup: true, + disabled: (record) => false, + component: shallowRef(defineAsyncComponent(() => import('@/views/network/dns/UpdateDnsZone.vue'))), + show: (record) => { return true } + }, + { + api: 'deleteDnsZone', + icon: 'delete-outlined', + label: 'label.dns.delete.zone', + message: 'message.action.delete.dns.zone', + dataView: true, + groupAction: true, + disabled: (record) => false, + show: (record) => { return true }, + groupMap: (selection) => { return selection.map(x => { return { id: x } }) } + } + ] } ] } diff --git a/ui/src/views/network/NicsTable.vue b/ui/src/views/network/NicsTable.vue index e9e15a0aa90..c209efb7849 100644 --- a/ui/src/views/network/NicsTable.vue +++ b/ui/src/views/network/NicsTable.vue @@ -58,8 +58,8 @@ {{ record.isolationuri }} - - {{ record.dns_url }} + + {{ record.dnsrecordurl }} diff --git a/ui/src/views/network/dns/AddDnsRecord.vue b/ui/src/views/network/dns/AddDnsRecord.vue new file mode 100644 index 00000000000..e6b1ff6b4ee --- /dev/null +++ b/ui/src/views/network/dns/AddDnsRecord.vue @@ -0,0 +1,192 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you 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. + + + + + + diff --git a/ui/src/views/network/dns/AddDnsServer.vue b/ui/src/views/network/dns/AddDnsServer.vue index 404870f9d82..b6c4751ab04 100644 --- a/ui/src/views/network/dns/AddDnsServer.vue +++ b/ui/src/views/network/dns/AddDnsServer.vue @@ -97,14 +97,18 @@ :title="$t('label.nameservers')" :tooltip="apiParams.nameservers?.description" /> - + mode="tags" + style="width: 100%" + :token-separators="[',', ' ']" + :placeholder="apiParams.nameservers?.description || 'ns1.example.com, ns2.example.com'" /> - {{ "Public server" }} + {{ $t('label.ispublic') }} @@ -163,32 +167,21 @@ export default { methods: { async handleSubmit () { if (this.loading) return - - // 1. Validate the form natively using Vue 3 $refs try { await this.$refs.formRef.validate() } catch (error) { - // Scroll to the first field with an error if (error.errorFields && error.errorFields.length > 0) { this.$refs.formRef.scrollToField(error.errorFields[0].name) } - return // Abort submission + return } - this.loading = true - - // 2. Execute the API try { - console.log('form data ', this.form) - // Pass the form object directly await postAPI('addDnsServer', this.form) - this.$notification.success({ message: this.$t('label.add.dns.server'), description: this.$t('message.success.add.dns.server') }) - - // Refresh the parent table and close modal this.$emit('refresh-data') this.closeAction() } catch (error) { diff --git a/ui/src/views/network/dns/AddDnsZone.vue b/ui/src/views/network/dns/AddDnsZone.vue new file mode 100644 index 00000000000..3991aeccb26 --- /dev/null +++ b/ui/src/views/network/dns/AddDnsZone.vue @@ -0,0 +1,179 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you 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. + + + + + + diff --git a/ui/src/views/network/dns/DnsRecordsTab.vue b/ui/src/views/network/dns/DnsRecordsTab.vue new file mode 100644 index 00000000000..5da2b7e9dbe --- /dev/null +++ b/ui/src/views/network/dns/DnsRecordsTab.vue @@ -0,0 +1,209 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you 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. + + + + diff --git a/ui/src/views/network/dns/UpdateDnsServer.vue b/ui/src/views/network/dns/UpdateDnsServer.vue new file mode 100644 index 00000000000..822cac06b01 --- /dev/null +++ b/ui/src/views/network/dns/UpdateDnsServer.vue @@ -0,0 +1,173 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you 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. + + + + + + diff --git a/ui/src/views/network/dns/UpdateDnsZone.vue b/ui/src/views/network/dns/UpdateDnsZone.vue new file mode 100644 index 00000000000..654d2a457dd --- /dev/null +++ b/ui/src/views/network/dns/UpdateDnsZone.vue @@ -0,0 +1,144 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you 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. + + + + + +