diff --git a/server/src/com/cloud/network/dao/NetworkDaoImpl.java b/server/src/com/cloud/network/dao/NetworkDaoImpl.java index ecb6a593eb9..90bb0166b6f 100644 --- a/server/src/com/cloud/network/dao/NetworkDaoImpl.java +++ b/server/src/com/cloud/network/dao/NetworkDaoImpl.java @@ -67,8 +67,9 @@ public class NetworkDaoImpl extends GenericDaoBase implements N private final GenericSearchBuilder NetworksCount; final SearchBuilder SourceNATSearch; final GenericSearchBuilder CountByZoneAndURI; + + ResourceTagsDaoImpl _tagsDao = ComponentLocator.inject(ResourceTagsDaoImpl.class); - NetworkAccountDaoImpl _accountsDao = ComponentLocator.inject(NetworkAccountDaoImpl.class); NetworkDomainDaoImpl _domainsDao = ComponentLocator.inject(NetworkDomainDaoImpl.class); NetworkOpDaoImpl _opDao = ComponentLocator.inject(NetworkOpDaoImpl.class); diff --git a/server/src/com/cloud/storage/dao/VMTemplateDaoImpl.java b/server/src/com/cloud/storage/dao/VMTemplateDaoImpl.java index cae54bc265f..49453f7e539 100755 --- a/server/src/com/cloud/storage/dao/VMTemplateDaoImpl.java +++ b/server/src/com/cloud/storage/dao/VMTemplateDaoImpl.java @@ -851,8 +851,10 @@ public class VMTemplateDaoImpl extends GenericDaoBase implem txn.start(); VMTemplateVO template = createForUpdate(); template.setRemoved(new Date()); - if (template != null) { - if (template.getFormat() == ImageFormat.ISO) { + + VMTemplateVO vo = findById(id); + if (vo != null) { + if (vo.getFormat() == ImageFormat.ISO) { _tagsDao.removeByIdAndType(id, TaggedResourceType.ISO); } else { _tagsDao.removeByIdAndType(id, TaggedResourceType.Template); diff --git a/server/src/com/cloud/vm/UserVmManagerImpl.java b/server/src/com/cloud/vm/UserVmManagerImpl.java index fb1399b7abd..7c813591b4f 100755 --- a/server/src/com/cloud/vm/UserVmManagerImpl.java +++ b/server/src/com/cloud/vm/UserVmManagerImpl.java @@ -2424,14 +2424,43 @@ public class UserVmManagerImpl implements UserVmManager, UserVmService, Manager String instanceName = VirtualMachineName.getVmName(id, owner.getId(), _instance); String uuidName = UUID.randomUUID().toString(); + + //verify hostname information if (hostName == null) { hostName = uuidName; } else { - // verify hostName (hostname doesn't have to be unique) + //1) check is hostName is RFC complient if (!NetUtils.verifyDomainNameLabel(hostName, true)) { throw new InvalidParameterValueException("Invalid name. Vm name can contain ASCII letters 'a' through 'z', the digits '0' through '9', " + "and the hyphen ('-'), must be between 1 and 63 characters long, and can't start or end with \"-\" and can't start with digit", null); } + //2) hostName has to be unique in the network domain + Map> ntwkDomains = new HashMap>(); + for (NetworkVO network : networkList) { + String ntwkDomain = network.getNetworkDomain(); + if (!ntwkDomains.containsKey(ntwkDomain)) { + List ntwkIds = new ArrayList(); + ntwkIds.add(network.getId()); + ntwkDomains.put(ntwkDomain, ntwkIds); + } else { + List ntwkIds = ntwkDomains.get(ntwkDomain); + ntwkIds.add(network.getId()); + ntwkDomains.put(ntwkDomain, ntwkIds); + } + } + + for (String ntwkDomain : ntwkDomains.keySet()) { + for (Long ntwkId : ntwkDomains.get(ntwkDomain)) { + //* get all vms hostNames in the network + List hostNames = _vmInstanceDao.listDistinctHostNames(ntwkId); + //* verify that there are no duplicates + if (hostNames.contains(hostName)) { + throw new InvalidParameterValueException("The vm with hostName " + hostName + + " already exists in the network domain: " + ntwkDomain + "; network=" + + _networkMgr.getNetwork(ntwkId)); + } + } + } } HypervisorType hypervisorType = null; diff --git a/server/src/com/cloud/vm/dao/VMInstanceDao.java b/server/src/com/cloud/vm/dao/VMInstanceDao.java index e9a6daeb4e2..149849df7cf 100644 --- a/server/src/com/cloud/vm/dao/VMInstanceDao.java +++ b/server/src/com/cloud/vm/dao/VMInstanceDao.java @@ -22,6 +22,7 @@ import com.cloud.utils.fsm.StateDao; import com.cloud.vm.VMInstanceVO; import com.cloud.vm.VirtualMachine; import com.cloud.vm.VirtualMachine.State; +import com.cloud.vm.VirtualMachine.Type; /* @@ -96,4 +97,11 @@ public interface VMInstanceDao extends GenericDao, StateDao< List listNonRemovedVmsByTypeAndNetwork(long networkId, VirtualMachine.Type... types); + /** + * @param networkId + * @param types + * @return + */ + List listDistinctHostNames(long networkId, VirtualMachine.Type... types); + } diff --git a/server/src/com/cloud/vm/dao/VMInstanceDaoImpl.java b/server/src/com/cloud/vm/dao/VMInstanceDaoImpl.java index 51e80fd65c1..b40d871e25d 100644 --- a/server/src/com/cloud/vm/dao/VMInstanceDaoImpl.java +++ b/server/src/com/cloud/vm/dao/VMInstanceDaoImpl.java @@ -74,7 +74,10 @@ public class VMInstanceDaoImpl extends GenericDaoBase implem protected GenericSearchBuilder CountRunningByHost; protected GenericSearchBuilder CountRunningByAccount; protected SearchBuilder NetworkTypeSearch; + protected GenericSearchBuilder DistinctHostNameSearch; + ResourceTagsDaoImpl _tagsDao = ComponentLocator.inject(ResourceTagsDaoImpl.class); + NicDao _nicDao = ComponentLocator.inject(NicDaoImpl.class); protected final Attribute _updateTimeAttr; @@ -94,6 +97,7 @@ public class VMInstanceDaoImpl extends GenericDaoBase implem protected final HostDaoImpl _hostDao = ComponentLocator.inject(HostDaoImpl.class); protected VMInstanceDaoImpl() { + IdStatesSearch = createSearchBuilder(); IdStatesSearch.and("id", IdStatesSearch.entity().getId(), Op.EQ); IdStatesSearch.and("states", IdStatesSearch.entity().getState(), Op.IN); @@ -529,7 +533,7 @@ public class VMInstanceDaoImpl extends GenericDaoBase implem @Override public List listNonRemovedVmsByTypeAndNetwork(long networkId, VirtualMachine.Type... types) { if (NetworkTypeSearch == null) { - NicDao _nicDao = ComponentLocator.getLocator("management-server").getDao(NicDao.class); + SearchBuilder nicSearch = _nicDao.createSearchBuilder(); nicSearch.and("networkId", nicSearch.entity().getNetworkId(), SearchCriteria.Op.EQ); @@ -550,6 +554,34 @@ public class VMInstanceDaoImpl extends GenericDaoBase implem return listBy(sc); } + + + @Override + public List listDistinctHostNames(long networkId, VirtualMachine.Type... types) { + if (DistinctHostNameSearch == null) { + + SearchBuilder nicSearch = _nicDao.createSearchBuilder(); + nicSearch.and("networkId", nicSearch.entity().getNetworkId(), SearchCriteria.Op.EQ); + + DistinctHostNameSearch = createSearchBuilder(String.class); + DistinctHostNameSearch.selectField(DistinctHostNameSearch.entity().getHostName()); + + DistinctHostNameSearch.and("types", DistinctHostNameSearch.entity().getType(), SearchCriteria.Op.IN); + DistinctHostNameSearch.and("removed", DistinctHostNameSearch.entity().getRemoved(), SearchCriteria.Op.NULL); + DistinctHostNameSearch.join("nicSearch", nicSearch, DistinctHostNameSearch.entity().getId(), + nicSearch.entity().getInstanceId(), JoinBuilder.JoinType.INNER); + DistinctHostNameSearch.done(); + } + + SearchCriteria sc = DistinctHostNameSearch.create(); + if (types != null && types.length != 0) { + sc.setParameters("types", (Object[]) types); + } + sc.setJoinParameters("nicSearch", "networkId", networkId); + + return customSearch(sc, null); + } + @Override @DB public boolean remove(Long id) { diff --git a/ui/scripts/network.js b/ui/scripts/network.js index 9a9facc9ad4..a0cde663062 100644 --- a/ui/scripts/network.js +++ b/ui/scripts/network.js @@ -152,6 +152,8 @@ } }; + var networkOfferingObjs = []; + cloudStack.sections.network = { title: 'label.network', id: 'network', @@ -257,9 +259,9 @@ state: 'Enabled' }, success: function(json) { - var networkOfferings = json.listnetworkofferingsresponse.networkoffering; + networkOfferingObjs = json.listnetworkofferingsresponse.networkoffering; args.response.success({ - data: $.map(networkOfferings, function(zone) { + data: $.map(networkOfferingObjs, function(zone) { return { id: zone.id, description: zone.name @@ -270,24 +272,74 @@ }); } }, + vpcid: { + label: 'VPC', + dependsOn: 'networkOfferingId', + select: function(args) { + var networkOfferingObj; + $(networkOfferingObjs).each(function(key, value) { + if(value.id == args.networkOfferingId) { + networkOfferingObj = value; + return false; //break each loop + } + }); + if(networkOfferingObj.forvpc == true) { + args.$select.closest('.form-item').css('display', 'inline-block'); + $.ajax({ + url: createURL('listVPCs'), + data: { + listAll: true + }, + success: function(json) { + var items = json.listvpcsresponse.vpc; + var data; + if(items != null && items.length > 0) { + data = $.map(items, function(item) { + return { + id: item.id, + description: item.name + } + }); + } + args.response.success({ data: data }); + } + }); + } + else { + args.$select.closest('.form-item').hide(); + args.response.success({ data: null }); + } + } + }, guestGateway: { label: 'label.guest.gateway' }, guestNetmask: { label: 'label.guest.netmask' } } }, action: function(args) { - var array1 = []; - array1.push("&zoneId=" + args.data.zoneId); - array1.push("&name=" + todb(args.data.name)); - array1.push("&displayText=" + todb(args.data.displayText)); - array1.push("&networkOfferingId=" + args.data.networkOfferingId); - - if(args.data.guestGateway != null && args.data.guestGateway.length > 0) - array1.push("&gateway=" + args.data.guestGateway); - if(args.data.guestNetmask != null && args.data.guestNetmask.length > 0) - array1.push("&netmask=" + args.data.guestNetmask); - + var dataObj = { + zoneId: args.data.zoneId, + name: args.data.name, + displayText: args.data.displayText, + networkOfferingId: args.data.networkOfferingId + }; + if(args.data.guestGateway != null && args.data.guestGateway.length > 0) { + $.extend(dataObj, { + gateway: args.data.guestGateway + }); + } + if(args.data.guestNetmask != null && args.data.guestNetmask.length > 0) { + $.extend(dataObj, { + netmask: args.data.guestNetmask + }); + } + if(args.$form.find('.form-item[rel=vpcid]').css("display") != "none") { + $.extend(dataObj, { + vpcid: args.data.vpcid + }); + } $.ajax({ - url: createURL('createNetwork' + array1.join("")), + url: createURL('createNetwork'), + data: dataObj, success: function(json) { args.response.success({ data: json.createnetworkresponse.network @@ -998,7 +1050,18 @@ actions: { add: { label: 'label.acquire.new.ip', - addRow: 'true', + addRow: 'true', + preFilter: function(args) { + if('networks' in args.context) { //from Guest Network section + if(args.context.networks[0].vpcid == null) //if it's a non-VPC network, show Acquire IP button + return true; + else //if it's a VPC network, hide Acquire IP button + return false; + } + else { //from VPC section + return true; //show Acquire IP button + } + }, messages: { confirm: function(args) { return 'message.acquire.new.ip'; @@ -1630,7 +1693,7 @@ var havingLbService = false; var havingVpnService = false; - if('networks' in args.context) { //from Guest Network section + if('networks' in args.context && args.context.networks[0].vpcid == null) { //a non-VPC network from Guest Network section $.ajax({ url: createURL("listNetworkOfferings&id=" + args.context.networks[0].networkofferingid), dataType: "json", @@ -1651,7 +1714,7 @@ } }); } - else { //from VPC section + else { //a VPC network from Guest Network section or from VPC section havingFirewallService = false; //firewall is not supported in IP from VPC section (because ACL has already supported in tier from VPC section) havingVpnService = false; //VPN is not supported in IP from VPC section @@ -3532,7 +3595,12 @@ ); } }); - } + }, + + notification: { + poll: pollAsyncJobResult + } + }, configureVpc: { label: 'Configure VPC', diff --git a/ui/scripts/ui/widgets/listView.js b/ui/scripts/ui/widgets/listView.js index 87c3e421424..2f66b0495b1 100644 --- a/ui/scripts/ui/widgets/listView.js +++ b/ui/scripts/ui/widgets/listView.js @@ -1163,7 +1163,7 @@ var showAdd = listViewData.actions.add.preFilter ? listViewData.actions.add.preFilter({ context: listViewData.context ? - listViewData.context : cloudStack.context + listViewData.context : args.context }) : true; if (showAdd) { diff --git a/ui/scripts/vpc.js b/ui/scripts/vpc.js index 8192c908a8e..40828b16f46 100644 --- a/ui/scripts/vpc.js +++ b/ui/scripts/vpc.js @@ -439,7 +439,12 @@ return true; else return false; - }, + }, + messages: { + notification: function(args) { + return 'Add new gateway'; + } + }, createForm: { title: 'Add new gateway', desc: 'Please specify the information to add a new gateway to this VPC.', @@ -476,10 +481,10 @@ args.response.error(parseXMLHttpResponse(json)); } }); - }, - messages: { - notification: function() { return 'Add gateway to VPC'; } - } + }, + notification: { + poll: pollAsyncJobResult + } } }, dataProvider: function(args) { @@ -552,11 +557,21 @@ $.ajax({ url: createURL('listPrivateGateways'), data: { - id: args.context.vpcGateways[0].id + id: args.context.vpcGateways[0].id, + listAll: true }, success: function(json) { var item = json.listprivategatewaysresponse.privategateway[0]; - args.response.success({ data: item }); + args.response.success({ + data: item, + actionFilter: function(args) { + var allowedActions = []; + if(isAdmin()) { + allowedActions.push("remove"); + } + return allowedActions; + } + }); } }); }