blob: 153a4065d1bc88494dd75742ae797892a9d907d7 [file] [log] [blame]
/*
* 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.openstack.nova.v2_0.compute.extensions;
import static com.google.common.base.Preconditions.checkNotNull;
import static com.google.common.base.Predicates.and;
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.transform;
import static org.jclouds.openstack.nova.v2_0.predicates.SecurityGroupPredicates.ruleCidr;
import static org.jclouds.openstack.nova.v2_0.predicates.SecurityGroupPredicates.ruleEndPort;
import static org.jclouds.openstack.nova.v2_0.predicates.SecurityGroupPredicates.ruleGroup;
import static org.jclouds.openstack.nova.v2_0.predicates.SecurityGroupPredicates.ruleProtocol;
import static org.jclouds.openstack.nova.v2_0.predicates.SecurityGroupPredicates.ruleStartPort;
import java.util.Set;
import javax.inject.Named;
import com.google.inject.Inject;
import org.jclouds.Constants;
import org.jclouds.compute.domain.SecurityGroup;
import org.jclouds.compute.extensions.SecurityGroupExtension;
import org.jclouds.compute.functions.GroupNamingConvention;
import org.jclouds.domain.Location;
import org.jclouds.location.Region;
import org.jclouds.net.domain.IpPermission;
import org.jclouds.net.domain.IpProtocol;
import org.jclouds.openstack.nova.v2_0.NovaApi;
import org.jclouds.openstack.nova.v2_0.domain.Ingress;
import org.jclouds.openstack.nova.v2_0.domain.SecurityGroupRule;
import org.jclouds.openstack.nova.v2_0.domain.regionscoped.RegionAndId;
import org.jclouds.openstack.nova.v2_0.domain.regionscoped.RegionAndName;
import org.jclouds.openstack.nova.v2_0.domain.regionscoped.RegionSecurityGroupNameAndPorts;
import org.jclouds.openstack.nova.v2_0.domain.regionscoped.SecurityGroupInRegion;
import org.jclouds.openstack.nova.v2_0.extensions.SecurityGroupApi;
import com.google.common.base.Function;
import com.google.common.base.Optional;
import com.google.common.base.Supplier;
import com.google.common.cache.LoadingCache;
import com.google.common.collect.FluentIterable;
import com.google.common.collect.ImmutableSet;
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 org.jclouds.compute.domain.SecurityGroup}s. Implementation
* is optional by providers.
*/
public class NovaSecurityGroupExtension implements SecurityGroupExtension {
protected final NovaApi api;
protected final ListeningExecutorService userExecutor;
protected final Supplier<Set<String>> regionIds;
protected final Function<SecurityGroupInRegion, SecurityGroup> groupConverter;
protected final LoadingCache<RegionAndName, SecurityGroup> groupCreator;
protected final GroupNamingConvention.Factory namingConvention;
@Inject
public NovaSecurityGroupExtension(NovaApi api,
@Named(Constants.PROPERTY_USER_THREADS) ListeningExecutorService userExecutor,
@Region Supplier<Set<String>> regionIds,
Function<SecurityGroupInRegion, SecurityGroup> groupConverter,
LoadingCache<RegionAndName, SecurityGroup> groupCreator,
GroupNamingConvention.Factory namingConvention) {
this.api = checkNotNull(api, "api");
this.userExecutor = checkNotNull(userExecutor, "userExecutor");
this.regionIds = checkNotNull(regionIds, "regionIds");
this.groupConverter = checkNotNull(groupConverter, "groupConverter");
this.groupCreator = checkNotNull(groupCreator, "groupCreator");
this.namingConvention = checkNotNull(namingConvention, "namingConvention");
}
@Override
public Set<SecurityGroup> listSecurityGroups() {
Iterable<? extends SecurityGroupInRegion> rawGroups = pollSecurityGroups();
Iterable<SecurityGroup> groups = transform(filter(rawGroups, notNull()),
groupConverter);
return ImmutableSet.copyOf(groups);
}
@Override
public Set<SecurityGroup> listSecurityGroupsInLocation(final Location location) {
String region = location.getId();
if (region == null) {
return ImmutableSet.of();
}
return listSecurityGroupsInLocation(region);
}
public Set<SecurityGroup> listSecurityGroupsInLocation(String region) {
Iterable<? extends SecurityGroupInRegion> rawGroups = pollSecurityGroupsByRegion(region);
Iterable<SecurityGroup> groups = transform(filter(rawGroups, notNull()),
groupConverter);
return ImmutableSet.copyOf(groups);
}
@Override
public Set<SecurityGroup> listSecurityGroupsForNode(String id) {
RegionAndId regionAndId = RegionAndId.fromSlashEncoded(checkNotNull(id, "id"));
String region = regionAndId.getRegion();
Set<org.jclouds.openstack.nova.v2_0.domain.SecurityGroup> allGroups = api.getServerApi(region).listSecurityGroupForServer(regionAndId.getId());
Set<? extends SecurityGroupInRegion> rawGroups =
FluentIterable.from(allGroups).transform(groupToGroupInRegion(allGroups, region)).toSet();
return ImmutableSet.copyOf(transform(filter(rawGroups, notNull()), groupConverter));
}
@Override
public SecurityGroup getSecurityGroupById(String id) {
RegionAndId regionAndId = RegionAndId.fromSlashEncoded(checkNotNull(id, "id"));
String region = regionAndId.getRegion();
String groupId = regionAndId.getId();
Optional<? extends SecurityGroupApi> sgApi = api.getSecurityGroupApi(region);
if (!sgApi.isPresent()) {
return null;
}
final FluentIterable<org.jclouds.openstack.nova.v2_0.domain.SecurityGroup> allGroups = sgApi.get().list();
SecurityGroupInRegion rawGroup = new SecurityGroupInRegion(sgApi.get().get(groupId), region, allGroups);
return groupConverter.apply(rawGroup);
}
@Override
public SecurityGroup createSecurityGroup(String name, Location location) {
String region = location.getId();
if (region == null) {
return null;
}
return createSecurityGroup(name, region);
}
public SecurityGroup createSecurityGroup(String name, String region) {
String markerGroup = namingConvention.create().sharedNameForGroup(name);
RegionSecurityGroupNameAndPorts regionAndName = new RegionSecurityGroupNameAndPorts(region, markerGroup, ImmutableSet.<Integer> of());
SecurityGroup rawGroup = groupCreator.getUnchecked(regionAndName);
return rawGroup;
}
@Override
public boolean removeSecurityGroup(String id) {
checkNotNull(id, "id");
RegionAndId regionAndId = RegionAndId.fromSlashEncoded(id);
String region = regionAndId.getRegion();
String groupId = regionAndId.getId();
Optional<? extends SecurityGroupApi> sgApi = api.getSecurityGroupApi(region);
if (!sgApi.isPresent()) {
return false;
}
// Would be nice to delete the group and invalidate the cache atomically - i.e. use a mutex.
// Will make sure that a create operation in parallel won't see inconsistent state.
boolean deleted = sgApi.get().delete(groupId);
for (SecurityGroup cachedSg : groupCreator.asMap().values()) {
if (id.equals(cachedSg.getId())) {
String groupName = cachedSg.getName();
groupCreator.invalidate(new RegionSecurityGroupNameAndPorts(region, groupName, ImmutableSet.<Integer>of()));
break;
}
}
return deleted;
}
@Override
public SecurityGroup addIpPermission(IpPermission ipPermission, SecurityGroup group) {
String region = group.getLocation().getId();
RegionAndId groupRegionAndId = RegionAndId.fromSlashEncoded(group.getId());
String id = groupRegionAndId.getId();
Optional<? extends SecurityGroupApi> sgApi = api.getSecurityGroupApi(region);
if (!sgApi.isPresent()) {
return null;
}
if (!ipPermission.getCidrBlocks().isEmpty()) {
for (String cidr : ipPermission.getCidrBlocks()) {
sgApi.get().createRuleAllowingCidrBlock(id,
Ingress.builder()
.ipProtocol(ipPermission.getIpProtocol())
.fromPort(ipPermission.getFromPort())
.toPort(ipPermission.getToPort())
.build(),
cidr);
}
}
if (!ipPermission.getGroupIds().isEmpty()) {
for (String regionAndGroupRaw : ipPermission.getGroupIds()) {
RegionAndId regionAndId = RegionAndId.fromSlashEncoded(regionAndGroupRaw);
String groupId = regionAndId.getId();
sgApi.get().createRuleAllowingSecurityGroupId(id,
Ingress.builder()
.ipProtocol(ipPermission.getIpProtocol())
.fromPort(ipPermission.getFromPort())
.toPort(ipPermission.getToPort())
.build(),
groupId);
}
}
return getSecurityGroupById(RegionAndId.fromRegionAndId(region, id).slashEncode());
}
@Override
public SecurityGroup addIpPermission(IpProtocol protocol, int startPort, int endPort,
Multimap<String, String> tenantIdGroupNamePairs,
Iterable<String> ipRanges,
Iterable<String> groupIds, SecurityGroup group) {
IpPermission.Builder permBuilder = IpPermission.builder();
permBuilder.ipProtocol(protocol);
permBuilder.fromPort(startPort);
permBuilder.toPort(endPort);
permBuilder.tenantIdGroupNamePairs(tenantIdGroupNamePairs);
permBuilder.cidrBlocks(ipRanges);
permBuilder.groupIds(groupIds);
return addIpPermission(permBuilder.build(), group);
}
@Override
public SecurityGroup removeIpPermission(IpPermission ipPermission, SecurityGroup group) {
String region = group.getLocation().getId();
RegionAndId groupRegionAndId = RegionAndId.fromSlashEncoded(group.getId());
String id = groupRegionAndId.getId();
Optional<? extends SecurityGroupApi> sgApi = api.getSecurityGroupApi(region);
if (!sgApi.isPresent()) {
return null;
}
org.jclouds.openstack.nova.v2_0.domain.SecurityGroup securityGroup = sgApi.get().get(id);
if (!ipPermission.getCidrBlocks().isEmpty()) {
for (String cidr : ipPermission.getCidrBlocks()) {
for (SecurityGroupRule rule : filter(securityGroup.getRules(),
and(ruleCidr(cidr), ruleProtocol(ipPermission.getIpProtocol()),
ruleStartPort(ipPermission.getFromPort()),
ruleEndPort(ipPermission.getToPort())))) {
sgApi.get().deleteRule(rule.getId());
}
}
}
if (!ipPermission.getGroupIds().isEmpty()) {
for (String groupId : ipPermission.getGroupIds()) {
for (SecurityGroupRule rule : filter(securityGroup.getRules(),
and(ruleGroup(groupId), ruleProtocol(ipPermission.getIpProtocol()),
ruleStartPort(ipPermission.getFromPort()),
ruleEndPort(ipPermission.getToPort())))) {
sgApi.get().deleteRule(rule.getId());
}
}
}
return getSecurityGroupById(RegionAndId.fromRegionAndId(region, id).slashEncode());
}
@Override
public SecurityGroup removeIpPermission(IpProtocol protocol, int startPort, int endPort,
Multimap<String, String> tenantIdGroupNamePairs,
Iterable<String> ipRanges,
Iterable<String> groupIds, SecurityGroup group) {
IpPermission.Builder permBuilder = IpPermission.builder();
permBuilder.ipProtocol(protocol);
permBuilder.fromPort(startPort);
permBuilder.toPort(endPort);
permBuilder.tenantIdGroupNamePairs(tenantIdGroupNamePairs);
permBuilder.cidrBlocks(ipRanges);
permBuilder.groupIds(groupIds);
return removeIpPermission(permBuilder.build(), group);
}
@Override
public boolean supportsTenantIdGroupNamePairs() {
return false;
}
@Override
public boolean supportsTenantIdGroupIdPairs() {
return false;
}
@Override
public boolean supportsGroupIds() {
return true;
}
@Override
public boolean supportsPortRangesForGroups() {
return false;
}
@Override
public boolean supportsExclusionCidrBlocks() {
return false;
}
protected Iterable<? extends SecurityGroupInRegion> pollSecurityGroups() {
Iterable<? extends Set<? extends SecurityGroupInRegion>> groups
= transform(regionIds.get(), allSecurityGroupsInRegion());
return concat(groups);
}
protected Iterable<? extends SecurityGroupInRegion> pollSecurityGroupsByRegion(String region) {
return allSecurityGroupsInRegion().apply(region);
}
protected Function<String, Set<? extends SecurityGroupInRegion>> allSecurityGroupsInRegion() {
return new Function<String, Set<? extends SecurityGroupInRegion>>() {
@Override
public Set<? extends SecurityGroupInRegion> apply(final String from) {
Optional<? extends SecurityGroupApi> sgApi = api.getSecurityGroupApi(from);
if (!sgApi.isPresent()) {
return ImmutableSet.of();
}
final FluentIterable<org.jclouds.openstack.nova.v2_0.domain.SecurityGroup> allGroups = sgApi.get().list();
return allGroups.transform(groupToGroupInRegion(allGroups, from)).toSet();
}
};
}
protected Function<org.jclouds.openstack.nova.v2_0.domain.SecurityGroup, SecurityGroupInRegion> groupToGroupInRegion(
final Iterable<org.jclouds.openstack.nova.v2_0.domain.SecurityGroup> allGroups, final String region) {
return new Function<org.jclouds.openstack.nova.v2_0.domain.SecurityGroup, SecurityGroupInRegion>() {
@Override
public SecurityGroupInRegion apply(org.jclouds.openstack.nova.v2_0.domain.SecurityGroup group) {
return new SecurityGroupInRegion(group, region, allGroups);
}
};
}
}