| /* |
| * 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); |
| } |
| }; |
| } |
| |
| } |