blob: dae8af790cbf0d986771283188fd54d4ea2bf1d1 [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.notNull;
import static com.google.common.collect.Iterables.filter;
import static com.google.common.collect.Iterables.transform;
import java.util.Map;
import java.util.Set;
import javax.annotation.Resource;
import javax.inject.Named;
import org.jclouds.Context;
import org.jclouds.compute.domain.SecurityGroup;
import org.jclouds.compute.extensions.SecurityGroupExtension;
import org.jclouds.compute.functions.GroupNamingConvention;
import org.jclouds.compute.reference.ComputeServiceConstants;
import org.jclouds.domain.Location;
import org.jclouds.javax.annotation.Nullable;
import org.jclouds.location.Region;
import org.jclouds.logging.Logger;
import org.jclouds.net.domain.IpPermission;
import org.jclouds.net.domain.IpProtocol;
import org.jclouds.openstack.neutron.v2.NeutronApi;
import org.jclouds.openstack.neutron.v2.domain.Rule;
import org.jclouds.openstack.neutron.v2.domain.RuleDirection;
import org.jclouds.openstack.neutron.v2.domain.RuleEthertype;
import org.jclouds.openstack.neutron.v2.domain.RuleProtocol;
import org.jclouds.openstack.neutron.v2.features.SecurityGroupApi;
import org.jclouds.openstack.nova.v2_0.NovaApi;
import org.jclouds.openstack.nova.v2_0.compute.functions.NeutronSecurityGroupToSecurityGroup;
import org.jclouds.openstack.nova.v2_0.compute.functions.NovaSecurityGroupToSecurityGroup;
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.rest.ApiContext;
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.Multimap;
import com.google.common.collect.Sets;
import com.google.inject.Inject;
/**
* 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 NeutronSecurityGroupExtension implements SecurityGroupExtension {
@Resource
@Named(ComputeServiceConstants.COMPUTE_LOGGER)
protected Logger logger = Logger.NULL;
private final NovaApi api;
private final Supplier<Set<String>> regionIds;
private final GroupNamingConvention.Factory namingConvention;
private final LoadingCache<RegionAndName, SecurityGroup> groupCreator;
private final Supplier<Map<String, Location>> locationIndex;
private final NeutronSecurityGroupToSecurityGroup.Factory neutronSecurityGroupToSecurityGroup;
private final NovaSecurityGroupToSecurityGroup.Factory novaSecurityGroupToSecurityGroup;
@Inject(optional = true)
@Named("openstack-neutron")
private Supplier<Context> neutronContextSupplier;
@Inject
NeutronSecurityGroupExtension(NovaApi api,
@Region Supplier<Set<String>> regionIds,
GroupNamingConvention.Factory namingConvention, LoadingCache<RegionAndName, SecurityGroup> groupCreator,
Supplier<Map<String, Location>> locationIndex,
NeutronSecurityGroupToSecurityGroup.Factory neutronSecurityGroupToSecurityGroup,
NovaSecurityGroupToSecurityGroup.Factory novaSecurityGroupToSecurityGroup) {
this.api = api;
this.regionIds = checkNotNull(regionIds, "regionIds");
this.namingConvention = checkNotNull(namingConvention, "namingConvention");
this.groupCreator = groupCreator;
this.locationIndex = locationIndex;
this.neutronSecurityGroupToSecurityGroup = neutronSecurityGroupToSecurityGroup;
this.novaSecurityGroupToSecurityGroup = novaSecurityGroupToSecurityGroup;
}
@Override
public SecurityGroup createSecurityGroup(String name, Location location) {
String region = location.getId();
if (region == null) {
return null;
}
logger.debug(">> creating security group %s in %s...", name, location);
String markerGroup = namingConvention.create().sharedNameForGroup(name);
RegionSecurityGroupNameAndPorts regionAndName = new RegionSecurityGroupNameAndPorts(region, markerGroup, ImmutableSet.<Integer> of());
return groupCreator.getUnchecked(regionAndName);
}
@Override
public Set<SecurityGroup> listSecurityGroups() {
Set<SecurityGroup> securityGroups = Sets.newHashSet();
for (String regionId : regionIds.get()) {
Location location = locationIndex.get().get(regionId);
securityGroups.addAll(listSecurityGroupsInLocation(location));
}
return ImmutableSet.copyOf(securityGroups);
}
@Override
public Set<SecurityGroup> listSecurityGroupsInLocation(final Location location) {
String region = location.getId();
if (region == null) {
return ImmutableSet.of();
}
return getSecurityGroupApi(region).listSecurityGroups().concat().transform(neutronSecurityGroupToSecurityGroup.create(location)).toSet();
}
@Override
public Set<SecurityGroup> listSecurityGroupsForNode(String id) {
RegionAndId regionAndId = RegionAndId.fromSlashEncoded(checkNotNull(id, "id"));
String region = regionAndId.getRegion();
Location location = locationIndex.get().get(region);
Set<org.jclouds.openstack.nova.v2_0.domain.SecurityGroup> allGroups = api.getServerApi(region).listSecurityGroupForServer(regionAndId.getId());
return ImmutableSet.copyOf(transform(filter(allGroups, notNull()), novaSecurityGroupToSecurityGroup.create(location)));
}
@Override
public SecurityGroup getSecurityGroupById(String id) {
RegionAndId regionAndId = RegionAndId.fromSlashEncoded(checkNotNull(id, "id"));
String region = regionAndId.getRegion();
String groupId = regionAndId.getId();
SecurityGroupApi securityGroupApi = getSecurityGroupApi(region);
Location location = locationIndex.get().get(region);
return neutronSecurityGroupToSecurityGroup.create(location).apply(securityGroupApi.getSecurityGroup(groupId));
}
@Override
public boolean removeSecurityGroup(String id) {
checkNotNull(id, "id");
RegionAndId regionAndId = RegionAndId.fromSlashEncoded(id);
String region = regionAndId.getRegion();
String groupId = regionAndId.getId();
SecurityGroupApi securityGroupApi = getSecurityGroupApi(region);
// 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 = securityGroupApi.deleteSecurityGroup(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();
SecurityGroupApi securityGroupApi = getSecurityGroupApi(region);
if (!ipPermission.getCidrBlocks().isEmpty()) {
for (String cidr : ipPermission.getCidrBlocks()) {
securityGroupApi.create(Rule.CreateRule.createBuilder(RuleDirection.INGRESS, group.getProviderId())
.protocol(RuleProtocol.fromValue(ipPermission.getIpProtocol().name()))
.ethertype(RuleEthertype.IPV4)
.portRangeMin(ipPermission.getFromPort())
.portRangeMax(ipPermission.getToPort())
.remoteIpPrefix(cidr)
.build());
}
}
if (!ipPermission.getGroupIds().isEmpty()) {
for (String regionAndGroupRaw : ipPermission.getGroupIds()) {
RegionAndId regionAndId = RegionAndId.fromSlashEncoded(regionAndGroupRaw);
String groupId = regionAndId.getId();
securityGroupApi.create(Rule.CreateRule.createBuilder(RuleDirection.INGRESS, groupId)
.protocol(RuleProtocol.fromValue(ipPermission.getIpProtocol().name()))
.ethertype(RuleEthertype.IPV4)
.portRangeMin(ipPermission.getFromPort())
.portRangeMax(ipPermission.getToPort())
.remoteGroupId(groupId)
.build());
}
}
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(final IpPermission ipPermission, SecurityGroup group) {
String region = group.getLocation().getId();
RegionAndId groupRegionAndId = RegionAndId.fromSlashEncoded(group.getId());
String id = groupRegionAndId.getId();
SecurityGroupApi securityGroupApi = getSecurityGroupApi(region);
org.jclouds.openstack.neutron.v2.domain.SecurityGroup securityGroup = securityGroupApi.getSecurityGroup(id);
if (!ipPermission.getCidrBlocks().isEmpty()) {
for (final String cidr : ipPermission.getCidrBlocks()) {
for (Rule rule : filter(securityGroup.getRules(),
new Predicate<Rule>() {
@Override
public boolean apply(@Nullable Rule input) {
return input.getRemoteIpPrefix() != null && input.getRemoteIpPrefix().equals(cidr) &&
input.getProtocol() != null && input.getProtocol().name().equals(ipPermission.getIpProtocol().name()) &&
input.getPortRangeMin() != null && input.getPortRangeMin() == ipPermission.getFromPort() &&
input.getPortRangeMax() != null && input.getPortRangeMax() == ipPermission.getToPort();
}
})) {
securityGroupApi.deleteRule(rule.getId());
}
}
}
if (!ipPermission.getGroupIds().isEmpty()) {
for (final String groupId : ipPermission.getGroupIds()) {
for (Rule rule : filter(securityGroup.getRules(),
new Predicate<Rule>() {
@Override
public boolean apply(@Nullable Rule input) {
return input.getRemoteGroupId() != null && input.getRemoteGroupId().equals(groupId) &&
input.getProtocol() != null && input.getProtocol().name().equals(ipPermission.getIpProtocol().name()) &&
input.getPortRangeMin() != null && input.getPortRangeMin() == ipPermission.getFromPort() &&
input.getPortRangeMax() != null && input.getPortRangeMax() == ipPermission.getToPort();
}
})) {
securityGroupApi.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;
}
private SecurityGroupApi getSecurityGroupApi(String region) {
return ((ApiContext<NeutronApi>) neutronContextSupplier.get()).getApi().getSecurityGroupApi(region);
}
}