use of com.cloud.dc.DataCenter.NetworkType in project cloudstack by apache.
the class OvsGuestNetworkGuru method implement.
@Override
public Network implement(Network network, NetworkOffering offering, DeployDestination dest, ReservationContext context) throws InsufficientVirtualNetworkCapacityException {
assert (network.getState() == State.Implementing) : "Why are we implementing " + network;
long dcId = dest.getDataCenter().getId();
NetworkType nwType = dest.getDataCenter().getNetworkType();
// get physical network id
Long physicalNetworkId = network.getPhysicalNetworkId();
// locate the physical network
if (physicalNetworkId == null) {
physicalNetworkId = _networkModel.findPhysicalNetworkId(dcId, offering.getTags(), offering.getTrafficType());
}
PhysicalNetworkVO physnet = _physicalNetworkDao.findById(physicalNetworkId);
if (!canHandle(offering, nwType, physnet)) {
s_logger.debug("Refusing to design this network");
return null;
}
NetworkVO implemented = (NetworkVO) super.implement(network, offering, dest, context);
if (network.getGateway() != null) {
implemented.setGateway(network.getGateway());
}
if (network.getCidr() != null) {
implemented.setCidr(network.getCidr());
}
implemented.setBroadcastDomainType(BroadcastDomainType.Vswitch);
// for the networks that are part of VPC enabled for distributed routing use scheme vs://vpcid.GRE key for network
if (network.getVpcId() != null && isVpcEnabledForDistributedRouter(network.getVpcId())) {
String keyStr = BroadcastDomainType.getValue(implemented.getBroadcastUri());
Long vpcid = network.getVpcId();
implemented.setBroadcastUri(BroadcastDomainType.Vswitch.toUri(vpcid.toString() + "." + keyStr));
}
return implemented;
}
use of com.cloud.dc.DataCenter.NetworkType in project cloudstack by apache.
the class NetworkServiceImpl method createPhysicalNetwork.
@Override
@DB
@ActionEvent(eventType = EventTypes.EVENT_PHYSICAL_NETWORK_CREATE, eventDescription = "Creating Physical Network", create = true)
public PhysicalNetwork createPhysicalNetwork(final Long zoneId, final String vnetRange, final String networkSpeed, final List<String> isolationMethods, String broadcastDomainRangeStr, final Long domainId, final List<String> tags, final String name) {
// Check if zone exists
if (zoneId == null) {
throw new InvalidParameterValueException("Please specify a valid zone.");
}
DataCenterVO zone = _dcDao.findById(zoneId);
if (zone == null) {
throw new InvalidParameterValueException("Please specify a valid zone.");
}
if (Grouping.AllocationState.Enabled == zone.getAllocationState()) {
// TBD: Send uuid instead of zoneId; may have to hardcode tablename in call to addProxyObject().
throw new PermissionDeniedException("Cannot create PhysicalNetwork since the Zone is currently enabled, zone Id: " + zoneId);
}
NetworkType zoneType = zone.getNetworkType();
if (zoneType == NetworkType.Basic) {
if (!_physicalNetworkDao.listByZone(zoneId).isEmpty()) {
// TBD: Send uuid instead of zoneId; may have to hardcode tablename in call to addProxyObject().
throw new CloudRuntimeException("Cannot add the physical network to basic zone id: " + zoneId + ", there is a physical network already existing in this basic Zone");
}
}
if (tags != null && tags.size() > 1) {
throw new InvalidParameterException("Only one tag can be specified for a physical network at this time");
}
if (isolationMethods != null && isolationMethods.size() > 1) {
throw new InvalidParameterException("Only one isolationMethod can be specified for a physical network at this time");
}
if (vnetRange != null) {
// Verify zone type
if (zoneType == NetworkType.Basic || (zoneType == NetworkType.Advanced && zone.isSecurityGroupEnabled())) {
throw new InvalidParameterValueException("Can't add vnet range to the physical network in the zone that supports " + zoneType + " network, Security Group enabled: " + zone.isSecurityGroupEnabled());
}
}
BroadcastDomainRange broadcastDomainRange = null;
if (broadcastDomainRangeStr != null && !broadcastDomainRangeStr.isEmpty()) {
try {
broadcastDomainRange = PhysicalNetwork.BroadcastDomainRange.valueOf(broadcastDomainRangeStr.toUpperCase());
} catch (IllegalArgumentException ex) {
throw new InvalidParameterValueException("Unable to resolve broadcastDomainRange '" + broadcastDomainRangeStr + "' to a supported value {Pod or Zone}");
}
// in Acton release you can specify only Zone broadcastdomain type in Advance zone, and Pod in Basic
if (zoneType == NetworkType.Basic && broadcastDomainRange != null && broadcastDomainRange != BroadcastDomainRange.POD) {
throw new InvalidParameterValueException("Basic zone can have broadcast domain type of value " + BroadcastDomainRange.POD + " only");
} else if (zoneType == NetworkType.Advanced && broadcastDomainRange != null && broadcastDomainRange != BroadcastDomainRange.ZONE) {
throw new InvalidParameterValueException("Advance zone can have broadcast domain type of value " + BroadcastDomainRange.ZONE + " only");
}
}
if (broadcastDomainRange == null) {
if (zoneType == NetworkType.Basic) {
broadcastDomainRange = PhysicalNetwork.BroadcastDomainRange.POD;
} else {
broadcastDomainRange = PhysicalNetwork.BroadcastDomainRange.ZONE;
}
}
try {
final BroadcastDomainRange broadcastDomainRangeFinal = broadcastDomainRange;
return Transaction.execute(new TransactionCallback<PhysicalNetworkVO>() {
@Override
public PhysicalNetworkVO doInTransaction(TransactionStatus status) {
// Create the new physical network in the database
long id = _physicalNetworkDao.getNextInSequence(Long.class, "id");
PhysicalNetworkVO pNetwork = new PhysicalNetworkVO(id, zoneId, vnetRange, networkSpeed, domainId, broadcastDomainRangeFinal, name);
pNetwork.setTags(tags);
pNetwork.setIsolationMethods(isolationMethods);
pNetwork = _physicalNetworkDao.persist(pNetwork);
// Add vnet entries for the new zone if zone type is Advanced
if (vnetRange != null) {
addOrRemoveVnets(vnetRange.split(","), pNetwork);
}
// add VirtualRouter as the default network service provider
addDefaultVirtualRouterToPhysicalNetwork(pNetwork.getId());
if (pNetwork.getIsolationMethods().contains("GRE"))
addDefaultOvsToPhysicalNetwork(pNetwork.getId());
// add security group provider to the physical network
addDefaultSecurityGroupProviderToPhysicalNetwork(pNetwork.getId());
// add VPCVirtualRouter as the defualt network service provider
addDefaultVpcVirtualRouterToPhysicalNetwork(pNetwork.getId());
// add baremetal as the defualt network service provider
addDefaultBaremetalProvidersToPhysicalNetwork(pNetwork.getId());
//Add Internal Load Balancer element as a default network service provider
addDefaultInternalLbProviderToPhysicalNetwork(pNetwork.getId());
return pNetwork;
}
});
} catch (Exception ex) {
s_logger.warn("Exception: ", ex);
throw new CloudRuntimeException("Fail to create a physical network");
}
}
use of com.cloud.dc.DataCenter.NetworkType in project cloudstack by apache.
the class ConfigurationManagerImpl method createZone.
@Override
@ActionEvent(eventType = EventTypes.EVENT_ZONE_CREATE, eventDescription = "creating zone", async = false)
public DataCenter createZone(final CreateZoneCmd cmd) {
// grab parameters from the command
final Long userId = CallContext.current().getCallingUserId();
final String zoneName = cmd.getZoneName();
final String dns1 = cmd.getDns1();
final String dns2 = cmd.getDns2();
final String ip6Dns1 = cmd.getIp6Dns1();
final String ip6Dns2 = cmd.getIp6Dns2();
final String internalDns1 = cmd.getInternalDns1();
final String internalDns2 = cmd.getInternalDns2();
final String guestCidr = cmd.getGuestCidrAddress();
final Long domainId = cmd.getDomainId();
final String type = cmd.getNetworkType();
Boolean isBasic = false;
String allocationState = cmd.getAllocationState();
final String networkDomain = cmd.getDomain();
boolean isSecurityGroupEnabled = cmd.getSecuritygroupenabled();
final boolean isLocalStorageEnabled = cmd.getLocalStorageEnabled();
if (allocationState == null) {
allocationState = Grouping.AllocationState.Disabled.toString();
}
if (!type.equalsIgnoreCase(NetworkType.Basic.toString()) && !type.equalsIgnoreCase(NetworkType.Advanced.toString())) {
throw new InvalidParameterValueException("Invalid zone type; only Advanced and Basic values are supported");
} else if (type.equalsIgnoreCase(NetworkType.Basic.toString())) {
isBasic = true;
}
final NetworkType zoneType = isBasic ? NetworkType.Basic : NetworkType.Advanced;
// error out when the parameter specified for Basic zone
if (zoneType == NetworkType.Basic && guestCidr != null) {
throw new InvalidParameterValueException("guestCidrAddress parameter is not supported for Basic zone");
}
DomainVO domainVO = null;
if (domainId != null) {
domainVO = _domainDao.findById(domainId);
}
if (zoneType == NetworkType.Basic) {
isSecurityGroupEnabled = true;
}
return createZone(userId, zoneName, dns1, dns2, internalDns1, internalDns2, guestCidr, domainVO != null ? domainVO.getName() : null, domainId, zoneType, allocationState, networkDomain, isSecurityGroupEnabled, isLocalStorageEnabled, ip6Dns1, ip6Dns2);
}
use of com.cloud.dc.DataCenter.NetworkType in project cloudstack by apache.
the class VmwareServerDiscoverer method find.
@Override
public Map<? extends ServerResource, Map<String, String>> find(long dcId, Long podId, Long clusterId, URI url, String username, String password, List<String> hostTags) throws DiscoveryException {
if (s_logger.isInfoEnabled())
s_logger.info("Discover host. dc: " + dcId + ", pod: " + podId + ", cluster: " + clusterId + ", uri host: " + url.getHost());
if (podId == null) {
if (s_logger.isInfoEnabled())
s_logger.info("No pod is assigned, assuming that it is not for vmware and skip it to next discoverer");
return null;
}
boolean failureInClusterDiscovery = true;
String vsmIp = "";
ClusterVO cluster = _clusterDao.findById(clusterId);
if (cluster == null || cluster.getHypervisorType() != HypervisorType.VMware) {
if (s_logger.isInfoEnabled())
s_logger.info("invalid cluster id or cluster is not for VMware hypervisors");
return null;
}
Map<String, String> clusterDetails = _clusterDetailsDao.findDetails(clusterId);
boolean legacyZone = _vmwareMgr.isLegacyZone(dcId);
boolean usernameNotProvided = (username == null || username.isEmpty());
boolean passwordNotProvided = (password == null || password.isEmpty());
//Check if NOT a legacy zone.
if (!legacyZone) {
// Retrieve VMware DC associated with specified zone
VmwareDatacenterVO vmwareDc = fetchVmwareDatacenterByZone(dcId);
// If either or both not provided, try to retrieve & use the credentials from database, which are provided earlier while adding VMware DC to zone.
if (usernameNotProvided || passwordNotProvided) {
// Retrieve credentials associated with VMware DC
s_logger.info("Username and/or Password not provided while adding cluster to cloudstack zone. " + "Hence using both username & password provided while adding VMware DC to CloudStack zone.");
username = vmwareDc.getUser();
password = vmwareDc.getPassword();
clusterDetails.put("username", username);
clusterDetails.put("password", password);
_clusterDetailsDao.persist(clusterId, clusterDetails);
}
String updatedInventoryPath = validateCluster(url, vmwareDc);
try {
if (!URLDecoder.decode(url.getPath(), "UTF-8").equals(updatedInventoryPath)) {
// If url from API doesn't specify DC then update url in database with DC associated with this zone.
clusterDetails.put("url", url.getScheme() + "://" + url.getHost() + updatedInventoryPath);
_clusterDetailsDao.persist(clusterId, clusterDetails);
}
} catch (UnsupportedEncodingException e) {
throw new DiscoveredWithErrorException("Unable to decode URL path, URL path : " + url.getPath(), e);
}
} else {
// For legacy zones insist on the old model of asking for credentials for each cluster being added.
if (usernameNotProvided) {
if (passwordNotProvided) {
throw new InvalidParameterValueException("Please provide username & password to add this cluster to zone");
} else {
throw new InvalidParameterValueException("Please provide username to add this cluster to zone");
}
} else if (passwordNotProvided) {
throw new InvalidParameterValueException("Please provide password to add this cluster to zone");
}
}
List<HostVO> hosts = _resourceMgr.listAllHostsInCluster(clusterId);
if (hosts != null && hosts.size() > 0) {
int maxHostsPerCluster = _hvCapabilitiesDao.getMaxHostsPerCluster(hosts.get(0).getHypervisorType(), hosts.get(0).getHypervisorVersion());
if (hosts.size() >= maxHostsPerCluster) {
String msg = "VMware cluster " + cluster.getName() + " is too big to add new host, current size: " + hosts.size() + ", max. size: " + maxHostsPerCluster;
s_logger.error(msg);
throw new DiscoveredWithErrorException(msg);
}
}
String privateTrafficLabel = null;
String publicTrafficLabel = null;
String guestTrafficLabel = null;
Map<String, String> vsmCredentials = null;
VirtualSwitchType defaultVirtualSwitchType = VirtualSwitchType.StandardVirtualSwitch;
String paramGuestVswitchType = null;
String paramGuestVswitchName = null;
String paramPublicVswitchType = null;
String paramPublicVswitchName = null;
VmwareTrafficLabel guestTrafficLabelObj = new VmwareTrafficLabel(TrafficType.Guest);
VmwareTrafficLabel publicTrafficLabelObj = new VmwareTrafficLabel(TrafficType.Public);
DataCenterVO zone = _dcDao.findById(dcId);
NetworkType zoneType = zone.getNetworkType();
_readGlobalConfigParameters();
// Private traffic will be only on standard vSwitch for now.
if (useDVS) {
// Parse url parameters for type of vswitch and name of vswitch specified at cluster level
paramGuestVswitchType = _urlParams.get(ApiConstants.VSWITCH_TYPE_GUEST_TRAFFIC);
paramGuestVswitchName = _urlParams.get(ApiConstants.VSWITCH_NAME_GUEST_TRAFFIC);
paramPublicVswitchType = _urlParams.get(ApiConstants.VSWITCH_TYPE_PUBLIC_TRAFFIC);
paramPublicVswitchName = _urlParams.get(ApiConstants.VSWITCH_NAME_PUBLIC_TRAFFIC);
defaultVirtualSwitchType = getDefaultVirtualSwitchType();
}
// Zone level vSwitch Type depends on zone level traffic labels
//
// User can override Zone wide vswitch type (for public and guest) by providing following optional parameters in addClusterCmd
// param "guestvswitchtype" with valid values vmwaredvs, vmwaresvs, nexusdvs
// param "publicvswitchtype" with valid values vmwaredvs, vmwaresvs, nexusdvs
//
// Format of label is <VSWITCH>,<VLANID>,<VSWITCHTYPE>
// If a field <VLANID> OR <VSWITCHTYPE> is not present leave it empty.
// Ex: 1) vswitch0
// 2) dvswitch0,200,vmwaredvs
// 3) nexusepp0,300,nexusdvs
// 4) vswitch1,400,vmwaresvs
// 5) vswitch0
// default vswitchtype is 'vmwaresvs'.
// <VSWITCHTYPE> 'vmwaresvs' is for vmware standard vswitch
// <VSWITCHTYPE> 'vmwaredvs' is for vmware distributed virtual switch
// <VSWITCHTYPE> 'nexusdvs' is for cisco nexus distributed virtual switch
// Get zone wide traffic labels for Guest traffic and Public traffic
guestTrafficLabel = _netmgr.getDefaultGuestTrafficLabel(dcId, HypervisorType.VMware);
// Process traffic label information provided at zone level and cluster level
guestTrafficLabelObj = getTrafficInfo(TrafficType.Guest, guestTrafficLabel, defaultVirtualSwitchType, paramGuestVswitchType, paramGuestVswitchName, clusterId);
if (zoneType == NetworkType.Advanced) {
// Get zone wide traffic label for Public traffic
publicTrafficLabel = _netmgr.getDefaultPublicTrafficLabel(dcId, HypervisorType.VMware);
// Process traffic label information provided at zone level and cluster level
publicTrafficLabelObj = getTrafficInfo(TrafficType.Public, publicTrafficLabel, defaultVirtualSwitchType, paramPublicVswitchType, paramPublicVswitchName, clusterId);
// Configuration Check: A physical network cannot be shared by different types of virtual switches.
//
// Check if different vswitch types are chosen for same physical network
// 1. Get physical network for guest traffic - multiple networks
// 2. Get physical network for public traffic - single network
// See if 2 is in 1
// if no - pass
// if yes - compare publicTrafficLabelObj.getVirtualSwitchType() == guestTrafficLabelObj.getVirtualSwitchType()
// true - pass
// false - throw exception - fail cluster add operation
List<? extends PhysicalNetwork> pNetworkListGuestTraffic = _netmgr.getPhysicalNtwksSupportingTrafficType(dcId, TrafficType.Guest);
List<? extends PhysicalNetwork> pNetworkListPublicTraffic = _netmgr.getPhysicalNtwksSupportingTrafficType(dcId, TrafficType.Public);
// Public network would be on single physical network hence getting first object of the list would suffice.
PhysicalNetwork pNetworkPublic = pNetworkListPublicTraffic.get(0);
if (pNetworkListGuestTraffic.contains(pNetworkPublic)) {
if (publicTrafficLabelObj.getVirtualSwitchType() != guestTrafficLabelObj.getVirtualSwitchType()) {
String msg = "Both public traffic and guest traffic is over same physical network " + pNetworkPublic + ". And virtual switch type chosen for each traffic is different" + ". A physical network cannot be shared by different types of virtual switches.";
s_logger.error(msg);
throw new InvalidParameterValueException(msg);
}
}
}
privateTrafficLabel = _netmgr.getDefaultManagementTrafficLabel(dcId, HypervisorType.VMware);
if (privateTrafficLabel != null) {
s_logger.info("Detected private network label : " + privateTrafficLabel);
}
Pair<Boolean, Long> vsmInfo = new Pair<Boolean, Long>(false, 0L);
if (nexusDVS && (guestTrafficLabelObj.getVirtualSwitchType() == VirtualSwitchType.NexusDistributedVirtualSwitch) || ((zoneType == NetworkType.Advanced) && (publicTrafficLabelObj.getVirtualSwitchType() == VirtualSwitchType.NexusDistributedVirtualSwitch))) {
// 2) Atleast 1 traffic type uses Nexus distributed virtual switch as backend.
if (zoneType != NetworkType.Basic) {
publicTrafficLabel = _netmgr.getDefaultPublicTrafficLabel(dcId, HypervisorType.VMware);
if (publicTrafficLabel != null) {
s_logger.info("Detected public network label : " + publicTrafficLabel);
}
}
// Get physical network label
guestTrafficLabel = _netmgr.getDefaultGuestTrafficLabel(dcId, HypervisorType.VMware);
if (guestTrafficLabel != null) {
s_logger.info("Detected guest network label : " + guestTrafficLabel);
}
// Before proceeding with validation of Nexus 1000v VSM check if an instance of Nexus 1000v VSM is already associated with this cluster.
boolean clusterHasVsm = _vmwareMgr.hasNexusVSM(clusterId);
if (!clusterHasVsm) {
vsmIp = _urlParams.get("vsmipaddress");
String vsmUser = _urlParams.get("vsmusername");
String vsmPassword = _urlParams.get("vsmpassword");
String clusterName = cluster.getName();
try {
vsmInfo = _nexusElement.validateAndAddVsm(vsmIp, vsmUser, vsmPassword, clusterId, clusterName);
} catch (ResourceInUseException ex) {
DiscoveryException discEx = new DiscoveryException(ex.getLocalizedMessage() + ". The resource is " + ex.getResourceName());
throw discEx;
}
}
vsmCredentials = _vmwareMgr.getNexusVSMCredentialsByClusterId(clusterId);
}
VmwareContext context = null;
try {
context = VmwareContextFactory.create(url.getHost(), username, password);
if (privateTrafficLabel != null)
context.registerStockObject("privateTrafficLabel", privateTrafficLabel);
if (nexusDVS) {
if (vsmCredentials != null) {
s_logger.info("Stocking credentials of Nexus VSM");
context.registerStockObject("vsmcredentials", vsmCredentials);
}
}
List<ManagedObjectReference> morHosts = _vmwareMgr.addHostToPodCluster(context, dcId, podId, clusterId, URLDecoder.decode(url.getPath(), "UTF-8"));
if (morHosts == null)
s_logger.info("Found 0 hosts.");
if (privateTrafficLabel != null)
context.uregisterStockObject("privateTrafficLabel");
if (morHosts == null) {
s_logger.error("Unable to find host or cluster based on url: " + URLDecoder.decode(url.getPath(), "UTF-8"));
return null;
}
ManagedObjectReference morCluster = null;
clusterDetails = _clusterDetailsDao.findDetails(clusterId);
if (clusterDetails.get("url") != null) {
URI uriFromCluster = new URI(UriUtils.encodeURIComponent(clusterDetails.get("url")));
morCluster = context.getHostMorByPath(URLDecoder.decode(uriFromCluster.getPath(), "UTF-8"));
if (morCluster == null || !morCluster.getType().equalsIgnoreCase("ClusterComputeResource")) {
s_logger.warn("Cluster url does not point to a valid vSphere cluster, url: " + clusterDetails.get("url"));
return null;
} else {
ClusterMO clusterMo = new ClusterMO(context, morCluster);
if (clusterMo.isHAEnabled()) {
clusterDetails.put("NativeHA", "true");
_clusterDetailsDao.persist(clusterId, clusterDetails);
}
}
}
if (!validateDiscoveredHosts(context, morCluster, morHosts)) {
if (morCluster == null)
s_logger.warn("The discovered host is not standalone host, can not be added to a standalone cluster");
else
s_logger.warn("The discovered host does not belong to the cluster");
return null;
}
Map<VmwareResource, Map<String, String>> resources = new HashMap<VmwareResource, Map<String, String>>();
for (ManagedObjectReference morHost : morHosts) {
Map<String, String> details = new HashMap<String, String>();
Map<String, Object> params = new HashMap<String, Object>();
HostMO hostMo = new HostMO(context, morHost);
details.put("url", hostMo.getHostName());
details.put("username", username);
details.put("password", password);
String guid = morHost.getType() + ":" + morHost.getValue() + "@" + url.getHost();
details.put("guid", guid);
params.put("url", hostMo.getHostName());
params.put("username", username);
params.put("password", password);
params.put("zone", Long.toString(dcId));
params.put("pod", Long.toString(podId));
params.put("cluster", Long.toString(clusterId));
params.put("guid", guid);
if (privateTrafficLabel != null) {
params.put("private.network.vswitch.name", privateTrafficLabel);
}
params.put("guestTrafficInfo", guestTrafficLabelObj);
params.put("publicTrafficInfo", publicTrafficLabelObj);
params.put("router.aggregation.command.each.timeout", _configDao.getValue(Config.RouterAggregationCommandEachTimeout.toString()));
VmwareResource resource = new VmwareResource();
try {
resource.configure("VMware", params);
} catch (ConfigurationException e) {
_alertMgr.sendAlert(AlertManager.AlertType.ALERT_TYPE_HOST, dcId, podId, "Unable to add " + url.getHost(), "Error is " + e.getMessage());
s_logger.warn("Unable to instantiate " + url.getHost(), e);
}
resource.start();
resources.put(resource, details);
}
// place a place holder guid derived from cluster ID
try {
cluster.setGuid(UUID.nameUUIDFromBytes(String.valueOf(clusterId).getBytes("UTF-8")).toString());
} catch (UnsupportedEncodingException e) {
throw new DiscoveredWithErrorException("Unable to create UUID based on string " + String.valueOf(clusterId) + ". Bad clusterId or UTF-8 encoding error.");
}
_clusterDao.update(clusterId, cluster);
// Flag cluster discovery success
failureInClusterDiscovery = false;
return resources;
} catch (DiscoveredWithErrorException e) {
throw e;
} catch (Exception e) {
s_logger.warn("Unable to connect to Vmware vSphere server. service address: " + url.getHost() + ". " + e);
return null;
} finally {
if (context != null)
context.close();
if (failureInClusterDiscovery && vsmInfo.first()) {
try {
s_logger.debug("Deleting Nexus 1000v VSM " + vsmIp + " because cluster discovery and addition to zone has failed.");
_nexusElement.deleteCiscoNexusVSM(vsmInfo.second().longValue());
} catch (Exception e) {
s_logger.warn("Deleting Nexus 1000v VSM " + vsmIp + " failed.");
}
}
}
}
Aggregations