/*
* 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.
*/
package org.jclouds.ec2.compute.extensions;
import static com.google.common.base.Preconditions.checkNotNull;
import static com.google.common.base.Predicates.notNull;
import static com.google.common.collect.Iterables.concat;
import static com.google.common.collect.Iterables.filter;
import static com.google.common.collect.Iterables.getOnlyElement;
import static com.google.common.collect.Iterables.transform;
import java.util.NoSuchElementException;
import java.util.Set;
import javax.inject.Inject;
import javax.inject.Named;
import org.jclouds.Constants;
import org.jclouds.aws.util.AWSUtils;
import org.jclouds.collect.Memoized;
import org.jclouds.compute.domain.SecurityGroup;
import org.jclouds.compute.extensions.SecurityGroupExtension;
import org.jclouds.compute.functions.GroupNamingConvention;
import org.jclouds.compute.functions.GroupNamingConvention.Factory;
import org.jclouds.domain.Location;
import org.jclouds.ec2.EC2Api;
import org.jclouds.ec2.compute.domain.RegionAndName;
import org.jclouds.ec2.compute.domain.RegionNameAndIngressRules;
import org.jclouds.ec2.domain.RunningInstance;
import org.jclouds.ec2.domain.UserIdGroupPair;
import org.jclouds.location.Region;
import org.jclouds.net.domain.IpPermission;
import org.jclouds.net.domain.IpProtocol;
import com.google.common.base.Function;
import com.google.common.base.Predicate;
import com.google.common.base.Supplier;
import com.google.common.cache.LoadingCache;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Iterables;
import com.google.common.collect.Multimap;
import com.google.common.util.concurrent.ListeningExecutorService;
/**
* An extension to compute service to allow for the manipulation of {@link SecurityGroup}s. Implementation
* is optional by providers.
*/
public class EC2SecurityGroupExtension implements SecurityGroupExtension {
protected final EC2Api client;
protected final ListeningExecutorService userExecutor;
protected final Supplier<Set<String>> regions;
protected final Function<org.jclouds.ec2.domain.SecurityGroup, SecurityGroup> groupConverter;
protected final Supplier<Set<? extends Location>> locations;
protected final LoadingCache<RegionAndName, String> groupCreator;
protected final Factory namingConvention;
@Inject
public EC2SecurityGroupExtension(EC2Api client,
@Named(Constants.PROPERTY_USER_THREADS) ListeningExecutorService userExecutor,
@Region Supplier<Set<String>> regions,
Function<org.jclouds.ec2.domain.SecurityGroup, SecurityGroup> groupConverter,
@Memoized Supplier<Set<? extends Location>> locations,
@Named("SECURITY") LoadingCache<RegionAndName, String> groupCreator,
GroupNamingConvention.Factory namingConvention) {
this.client = checkNotNull(client, "client");
this.userExecutor = checkNotNull(userExecutor, "userExecutor");
this.regions = checkNotNull(regions, "regions");
this.groupConverter = checkNotNull(groupConverter, "groupConverter");
this.locations = checkNotNull(locations, "locations");
this.groupCreator = checkNotNull(groupCreator, "groupCreator");
this.namingConvention = checkNotNull(namingConvention, "namingConvention");
}
@Override
public Set<SecurityGroup> listSecurityGroups() {
Iterable<? extends org.jclouds.ec2.domain.SecurityGroup> rawGroups = pollSecurityGroups();
Iterable<SecurityGroup> groups = transform(filter(rawGroups, notNull()),
groupConverter);
return ImmutableSet.copyOf(groups);
}
@Override
public Set<SecurityGroup> listSecurityGroupsInLocation(final Location location) {
String region = AWSUtils.getRegionFromLocationOrNull(location);
if (region == null) {
return ImmutableSet.of();
}
return listSecurityGroupsInLocation(region);
}
public Set<SecurityGroup> listSecurityGroupsInLocation(String region) {
Iterable<? extends org.jclouds.ec2.domain.SecurityGroup> rawGroups = pollSecurityGroupsByRegion(region);
Iterable<SecurityGroup> groups = transform(filter(rawGroups, notNull()),
groupConverter);
return ImmutableSet.copyOf(groups);
}
@Override
public Set<SecurityGroup> listSecurityGroupsForNode(String id) {
checkNotNull(id, "id");
String[] parts = AWSUtils.parseHandle(id);
String region = parts[0];
String instanceId = parts[1];
RunningInstance instance = getOnlyElement(Iterables.concat(client.getInstanceApi().get().describeInstancesInRegion(region, instanceId)));
if (instance == null) {
return ImmutableSet.of();
}
Set<String> groupNames = instance.getGroupNames();
Set<? extends org.jclouds.ec2.domain.SecurityGroup> rawGroups =
client.getSecurityGroupApi().get().describeSecurityGroupsInRegion(region, Iterables.toArray(groupNames, String.class));
return ImmutableSet.copyOf(transform(filter(rawGroups, notNull()), groupConverter));
}
@Override
public SecurityGroup getSecurityGroupById(String id) {
checkNotNull(id, "id");
String[] parts = AWSUtils.parseHandle(id);
String region = parts[0];
String groupId = parts[1];
Set<? extends org.jclouds.ec2.domain.SecurityGroup> rawGroups =
client.getSecurityGroupApi().get().describeSecurityGroupsInRegion(region, groupId);
return getOnlyElement(transform(filter(rawGroups, notNull()), groupConverter));
}
@Override
public SecurityGroup createSecurityGroup(String name, Location location) {
String region = AWSUtils.getRegionFromLocationOrNull(location);
if (region != null) {
return createSecurityGroup(name, region);
} else {
return null;
}
}
public SecurityGroup createSecurityGroup(String name, String region) {
String markerGroup = namingConvention.create().sharedNameForGroup(name);
RegionNameAndIngressRules regionAndName = new RegionNameAndIngressRules(region, markerGroup, new int[] {},
false);
groupCreator.getUnchecked(regionAndName);
return getSecurityGroupById(regionAndName.slashEncode());
}
@Override
public boolean removeSecurityGroup(String id) {
checkNotNull(id, "id");
String[] parts = AWSUtils.parseHandle(id);
String region = parts[0];
String groupName = parts[1];
if (client.getSecurityGroupApi().get().describeSecurityGroupsInRegion(region, groupName).size() > 0) {
client.getSecurityGroupApi().get().deleteSecurityGroupInRegion(region, groupName);
// TODO: test this clear happens
groupCreator.invalidate(new RegionNameAndIngressRules(region, groupName, null, false));
return true;
}
return false;
}
@Override
public SecurityGroup addIpPermission(IpPermission ipPermission, SecurityGroup group) {
String region = AWSUtils.getRegionFromLocationOrNull(group.getLocation());
String name = group.getName();
if (ipPermission.getCidrBlocks().size() > 0) {
for (String cidr : ipPermission.getCidrBlocks()) {
client.getSecurityGroupApi().get().
authorizeSecurityGroupIngressInRegion(region,
name,
ipPermission.getIpProtocol(),
ipPermission.getFromPort(),
ipPermission.getToPort(),
cidr);
}
}
if (ipPermission.getTenantIdGroupNamePairs().size() > 0) {
for (String userId : ipPermission.getTenantIdGroupNamePairs().keySet()) {
for (String groupName : ipPermission.getTenantIdGroupNamePairs().get(userId)) {
client.getSecurityGroupApi().get().
authorizeSecurityGroupIngressInRegion(region,
name,
new UserIdGroupPair(userId, groupName));
}
}
}
return getSecurityGroupById(new RegionAndName(region, group.getName()).slashEncode());
}
@Override
public SecurityGroup addIpPermission(IpProtocol protocol, int startPort, int endPort,
Multimap<String, String> tenantIdGroupNamePairs,
Iterable<String> ipRanges,
Iterable<String> groupIds, SecurityGroup group) {
String region = AWSUtils.getRegionFromLocationOrNull(group.getLocation());
String name = group.getName();
if (Iterables.size(ipRanges) > 0) {
for (String cidr : ipRanges) {
client.getSecurityGroupApi().get().
authorizeSecurityGroupIngressInRegion(region,
name,
protocol,
startPort,
endPort,
cidr);
}
}
if (tenantIdGroupNamePairs.size() > 0) {
for (String userId : tenantIdGroupNamePairs.keySet()) {
for (String groupName : tenantIdGroupNamePairs.get(userId)) {
client.getSecurityGroupApi().get().
authorizeSecurityGroupIngressInRegion(region,
name,
new UserIdGroupPair(userId, groupName));
}
}
}
return getSecurityGroupById(new RegionAndName(region, group.getName()).slashEncode());
}
@Override
public SecurityGroup removeIpPermission(IpPermission ipPermission, SecurityGroup group) {
String region = AWSUtils.getRegionFromLocationOrNull(group.getLocation());
String name = group.getName();
if (ipPermission.getCidrBlocks().size() > 0) {
for (String cidr : ipPermission.getCidrBlocks()) {
client.getSecurityGroupApi().get().
revokeSecurityGroupIngressInRegion(region,
name,
ipPermission.getIpProtocol(),
ipPermission.getFromPort(),
ipPermission.getToPort(),
cidr);
}
}
if (ipPermission.getTenantIdGroupNamePairs().size() > 0) {
for (String userId : ipPermission.getTenantIdGroupNamePairs().keySet()) {
for (String groupName : ipPermission.getTenantIdGroupNamePairs().get(userId)) {
client.getSecurityGroupApi().get().
revokeSecurityGroupIngressInRegion(region,
name,
new UserIdGroupPair(userId, groupName));
}
}
}
return getSecurityGroupById(new RegionAndName(region, group.getName()).slashEncode());
}
@Override
public SecurityGroup removeIpPermission(IpProtocol protocol, int startPort, int endPort,
Multimap<String, String> tenantIdGroupNamePairs,
Iterable<String> ipRanges,
Iterable<String> groupIds, SecurityGroup group) {
String region = AWSUtils.getRegionFromLocationOrNull(group.getLocation());
String name = group.getName();
if (Iterables.size(ipRanges) > 0) {
for (String cidr : ipRanges) {
client.getSecurityGroupApi().get().
revokeSecurityGroupIngressInRegion(region,
name,
protocol,
startPort,
endPort,
cidr);
}
}
if (tenantIdGroupNamePairs.size() > 0) {
for (String userId : tenantIdGroupNamePairs.keySet()) {
for (String groupName : tenantIdGroupNamePairs.get(userId)) {
client.getSecurityGroupApi().get().
revokeSecurityGroupIngressInRegion(region,
name,
new UserIdGroupPair(userId, groupName));
}
}
}
return getSecurityGroupById(new RegionAndName(region, group.getName()).slashEncode());
}
@Override
public boolean supportsTenantIdGroupNamePairs() {
return true;
}
@Override
public boolean supportsTenantIdGroupIdPairs() {
return false;
}
@Override
public boolean supportsGroupIds() {
return false;
}
@Override
public boolean supportsPortRangesForGroups() {
return false;
}
protected Iterable<? extends org.jclouds.ec2.domain.SecurityGroup> pollSecurityGroups() {
Iterable<? extends Set<? extends org.jclouds.ec2.domain.SecurityGroup>> groups
= transform(regions.get(), allSecurityGroupsInRegion());
return concat(groups);
}
protected Iterable<? extends org.jclouds.ec2.domain.SecurityGroup> pollSecurityGroupsByRegion(String region) {
return allSecurityGroupsInRegion().apply(region);
}
protected Function<String, Set<? extends org.jclouds.ec2.domain.SecurityGroup>> allSecurityGroupsInRegion() {
return new Function<String, Set<? extends org.jclouds.ec2.domain.SecurityGroup>>() {
@Override
public Set<? extends org.jclouds.ec2.domain.SecurityGroup> apply(String from) {
return client.getSecurityGroupApi().get().describeSecurityGroupsInRegion(from);
}
};
}
protected Location findLocationWithId(final String locationId) {
if (locationId == null)
return null;
try {
Location location = Iterables.find(locations.get(), new Predicate<Location>() {
@Override
public boolean apply(Location input) {
return input.getId().equals(locationId);
}
});
return location;
} catch (NoSuchElementException e) {
return null;
}
}
}