@Override public void allocate(Allocation allocInfo) throws Exception { try { final VmInstanceLifecycleHelper helper = VmInstanceLifecycleHelpers.get(); final PrepareNetworkResourcesType request = new PrepareNetworkResourcesType(); request.setAvailabilityZone(allocInfo.getPartition().getName()); request.setVpc( allocInfo.getSubnet() == null ? null : CloudMetadatas.toDisplayName().apply(allocInfo.getSubnet().getVpc())); request.setSubnet(CloudMetadatas.toDisplayName().apply(allocInfo.getSubnet())); request.setFeatures(Lists.<NetworkFeature>newArrayList(new DnsHostNamesFeature())); helper.prepareNetworkAllocation(allocInfo, request); final PrepareNetworkResourcesResultType result = Networking.getInstance().prepare(request); for (final ResourceToken token : allocInfo.getAllocationTokens()) { for (final NetworkResource networkResource : result.getResources()) { if (token.getInstanceId().equals(networkResource.getOwnerId())) { token .getAttribute(NetworkResourceVmInstanceLifecycleHelper.NetworkResourcesKey) .add(networkResource); } } } } catch (Exception e) { throw Objects.firstNonNull(Exceptions.findCause(e, NotEnoughResourcesException.class), e); } }
@Override public boolean apply(Allocation allocInfo) throws MetadataException { Context ctx = allocInfo.getContext(); NetworkGroups.lookup( ctx.getUserFullName().asAccountFullName(), NetworkGroups.defaultNetworkName()); Set<String> networkNames = Sets.newHashSet(allocInfo.getRequest().getGroupSet()); if (networkNames.isEmpty()) { networkNames.add(NetworkGroups.defaultNetworkName()); } Map<String, NetworkGroup> networkRuleGroups = Maps.newHashMap(); for (String groupName : networkNames) { NetworkGroup group = NetworkGroups.lookup(ctx.getUserFullName().asAccountFullName(), groupName); if (!ctx.hasAdministrativePrivileges() && !RestrictedTypes.filterPrivileged().apply(group)) { throw new IllegalMetadataAccessException( "Not authorized to use network group " + groupName + " for " + ctx.getUser().getName()); } networkRuleGroups.put(groupName, group); } Set<String> missingNets = Sets.difference(networkNames, networkRuleGroups.keySet()); if (!missingNets.isEmpty()) { throw new NoSuchMetadataException("Failed to find security group info for: " + missingNets); } else { allocInfo.setNetworkRules(networkRuleGroups); } return true; }
@SuppressWarnings("unchecked") @Override public void allocate(Allocation allocInfo) throws Exception { RestrictedTypes.allocateNamedUnitlessResources( allocInfo.getAllocationTokens().size(), allocInfo.getVmType().allocator(), (Predicate) Predicates.alwaysTrue()); }
@Override public boolean apply(Allocation allocInfo) throws MetadataException, AuthException, VerificationException { RunInstancesType msg = allocInfo.getRequest(); String imageId = msg.getImageId(); VmType vmType = allocInfo.getVmType(); try { BootableSet bootSet = Emis.newBootableSet(imageId); allocInfo.setBootableSet(bootSet); // Add (1024L * 1024L * 10) to handle NTFS min requirements. if (!bootSet.isBlockStorage()) { if (Platform.windows.equals(bootSet.getMachine().getPlatform()) && bootSet.getMachine().getImageSizeBytes() > ((1024L * 1024L * 1024L * vmType.getDisk()) + (1024L * 1024L * 10))) { throw new ImageInstanceTypeVerificationException( "Unable to run instance " + bootSet.getMachine().getDisplayName() + " in which the size " + bootSet.getMachine().getImageSizeBytes() + " bytes of the instance is greater than the vmType " + vmType.getDisplayName() + " size " + vmType.getDisk() + " GB."); } else if (bootSet.getMachine().getImageSizeBytes() > ((1024L * 1024L * 1024L * vmType.getDisk()))) { throw new ImageInstanceTypeVerificationException( "Unable to run instance " + bootSet.getMachine().getDisplayName() + " in which the size " + bootSet.getMachine().getImageSizeBytes() + " bytes of the instance is greater than the vmType " + vmType.getDisplayName() + " size " + vmType.getDisk() + " GB."); } } } catch (VerificationException e) { throw e; } catch (MetadataException ex) { LOG.error(ex); throw ex; } catch (RuntimeException ex) { LOG.error(ex); throw new VerificationException( "Failed to verify references for request: " + msg.toSimpleString() + " because of: " + ex.getMessage(), ex); } return true; }
@Override public boolean apply(Allocation allocInfo) throws MetadataException { Context ctx = allocInfo.getContext(); User user = ctx.getUser(); String instanceType = allocInfo.getRequest().getInstanceType(); VmType vmType = VmTypes.lookup(instanceType); if (!ctx.hasAdministrativePrivileges() && !RestrictedTypes.filterPrivileged().apply(vmType)) { throw new IllegalMetadataAccessException( "Not authorized to allocate vm type " + instanceType + " for " + ctx.getUserFullName()); } allocInfo.setVmType(vmType); return true; }
@Override public boolean apply(Allocation allocInfo) throws MetadataException { if (allocInfo.getRequest().getKeyName() == null || "".equals(allocInfo.getRequest().getKeyName())) { if (ImageMetadata.Platform.windows.equals( allocInfo.getBootSet().getMachine().getPlatform())) { throw new InvalidMetadataException( "You must specify a keypair when running a windows vm: " + allocInfo.getRequest().getImageId()); } else { allocInfo.setSshKeyPair(KeyPairs.noKey()); return true; } } Context ctx = allocInfo.getContext(); RunInstancesType request = allocInfo.getRequest(); String keyName = request.getKeyName(); SshKeyPair key = KeyPairs.lookup(ctx.getUserFullName().asAccountFullName(), keyName); if (!ctx.hasAdministrativePrivileges() && !RestrictedTypes.filterPrivileged().apply(key)) { throw new IllegalMetadataAccessException( "Not authorized to use keypair " + keyName + " by " + ctx.getUser().getName()); } allocInfo.setSshKeyPair(key); return true; }
@Override public boolean apply(Allocation allocInfo) throws MetadataException { String instanceType = allocInfo.getRequest().getInstanceType(); VmType vmType = VmTypes.lookup(instanceType); if (!RestrictedTypes.filterPrivileged().apply(vmType)) { throw new IllegalMetadataAccessException( "Not authorized to allocate vm type " + instanceType + " for " + allocInfo.getOwnerFullName()); } allocInfo.setVmType(vmType); return true; }
@Override public boolean apply(Allocation allocInfo) throws MetadataException { byte[] userData = allocInfo.getUserData(); if (userData != null && userData.length > Integer.parseInt(VmInstances.USER_DATA_MAX_SIZE_KB) * 1024) { throw new InvalidMetadataException( "User data may not exceed " + VmInstances.USER_DATA_MAX_SIZE_KB + " KB"); } return true; }
@Override public boolean apply(Allocation allocInfo) throws MetadataException { RunInstancesType request = allocInfo.getRequest(); String zoneName = request.getAvailabilityZone(); if (Clusters.getInstance().listValues().isEmpty()) { LOG.debug("enabled values: " + Joiner.on("\n").join(Clusters.getInstance().listValues())); LOG.debug("disabled values: " + Joiner.on("\n").join(Clusters.getInstance().listValues())); throw new VerificationException( "Not enough resources: no cluster controller is currently available to run instances."); } else if (Partitions.exists(zoneName)) { Partition partition = Partitions.lookupByName(zoneName); allocInfo.setPartition(partition); } else if (Partition.DEFAULT_NAME.equals(zoneName)) { Partition partition = Partition.DEFAULT; allocInfo.setPartition(partition); } else { throw new VerificationException( "Not enough resources: no cluster controller is currently available to run instances."); } return true; }
@Override public boolean apply(Allocation allocInfo) throws MetadataException { if (allocInfo.getRequest().getKeyName() == null || "".equals(allocInfo.getRequest().getKeyName())) { allocInfo.setSshKeyPair(KeyPairs.noKey()); return true; } UserFullName ownerFullName = allocInfo.getOwnerFullName(); RunInstancesType request = allocInfo.getRequest(); String keyName = request.getKeyName(); SshKeyPair key = KeyPairs.lookup(ownerFullName.asAccountFullName(), keyName); if (!RestrictedTypes.filterPrivileged().apply(key)) { throw new IllegalMetadataAccessException( "Not authorized to use keypair " + keyName + " by " + ownerFullName.getUserName()); } allocInfo.setSshKeyPair(key); return true; }
@Override public void fail(Allocation allocInfo, Throwable t) { allocInfo.abort(); }
@Override public void allocate(Allocation allocInfo) throws Exception { Partition reqPartition = allocInfo.getPartition(); String zoneName = reqPartition.getName(); String vmTypeName = allocInfo.getVmType().getName(); /* Validate min and max amount */ final int minAmount = allocInfo.getMinCount(); final int maxAmount = allocInfo.getMaxCount(); if (minAmount > maxAmount) throw new RuntimeException( "Maximum instance count must not be smaller than minimum instance count"); /* Retrieve our context and list of clusters associated with this zone */ List<Cluster> authorizedClusters = this.doPrivilegedLookup(zoneName, vmTypeName); int remaining = maxAmount; int allocated = 0; int available; LOG.info( "Found authorized clusters: " + Iterables.transform(authorizedClusters, HasName.GET_NAME)); /* Do we have any VM available throughout our clusters? */ if ((available = checkAvailability(vmTypeName, authorizedClusters)) < minAmount) { throw new NotEnoughResourcesException( "Not enough resources (" + available + " in " + zoneName + " < " + minAmount + "): vm instances."); } else { for (Cluster cluster : authorizedClusters) { if (remaining <= 0) { break; } else { ResourceState state = cluster.getNodeState(); Partition partition = cluster.getConfiguration().lookupPartition(); /* Has a partition been set if the AZ was not specified? */ if (allocInfo.getPartition().equals(Partition.DEFAULT)) { /* * Ok, do we have enough slots in this partition to support our request? We should have at least * the minimum. The list is sorted in order of resource availability from the cluster with the most * available to the cluster with the least amount available. This is why we don't check against the * maxAmount value since its a best effort at this point. If we select the partition here and we * can't fit maxAmount, based on the sorting order, the next partition will not fit maxAmount anyway. */ int zoneAvailable = checkZoneAvailability(vmTypeName, partition, authorizedClusters); if (zoneAvailable < minAmount) continue; /* Lets use this partition */ allocInfo.setPartition(partition); } else if (!allocInfo.getPartition().equals(partition)) { /* We should only pick clusters that are part of the selected AZ */ continue; } if (allocInfo.getBootSet().getMachine() instanceof BlockStorageImageInfo) { try { Topology.lookup(Storage.class, partition); } catch (Exception ex) { allocInfo.abort(); allocInfo.setPartition(reqPartition); throw new NotEnoughResourcesException( "Not enough resources: Cannot run EBS instances in partition w/o a storage controller: " + ex.getMessage(), ex); } } try { int tryAmount = (remaining > state.getAvailability(vmTypeName).getAvailable()) ? state.getAvailability(vmTypeName).getAvailable() : remaining; List<ResourceToken> tokens = this.requestResourceToken(allocInfo, tryAmount, maxAmount); remaining -= tokens.size(); allocated += tokens.size(); } catch (Exception t) { LOG.error(t); Logs.extreme().error(t, t); allocInfo.abort(); allocInfo.setPartition(reqPartition); /* if we still have some allocation remaining AND no more resources are available */ if (((available = checkZoneAvailability(vmTypeName, partition, authorizedClusters)) < remaining) && (remaining > 0)) { throw new NotEnoughResourcesException( "Not enough resources (" + available + " in " + zoneName + " < " + minAmount + "): vm instances.", t); } else { throw new NotEnoughResourcesException(t.getMessage(), t); } } } } /* Were we able to meet our minimum requirements? */ if ((allocated < minAmount) && (remaining > 0)) { allocInfo.abort(); allocInfo.setPartition(reqPartition); if (reqPartition.equals(Partition.DEFAULT)) { throw new NotEnoughResourcesException( "Not enough resources available in all zone for " + minAmount + "): vm instances."); } else { available = checkZoneAvailability(vmTypeName, reqPartition, authorizedClusters); throw new NotEnoughResourcesException( "Not enough resources (" + available + " in " + zoneName + " < " + minAmount + "): vm instances."); } } } }
private List<ResourceToken> requestResourceToken( final Allocation allocInfo, final int tryAmount, final int maxAmount) throws Exception { ServiceConfiguration config = Topology.lookup(ClusterController.class, allocInfo.getPartition()); Cluster cluster = Clusters.lookup(config); /** * TODO:GRZE: this is the call path which needs to trigger gating. * It shouldn't be handled directly here, but instead be handled in {@link ResourceState#requestResourceAllocation(). * */ if (cluster.getGateLock().readLock().tryLock(60, TimeUnit.SECONDS)) { try { final ResourceState state = cluster.getNodeState(); /** * NOTE: If the defined instance type has an ordering conflict w/ some other type then it * isn't safe to service TWO requests which use differing types during the same resource * refresh duty cycle. This determines whether or not an asynchronous allocation is safe * to do for the request instance type or whether a synchronous resource availability * refresh is needed. */ boolean unorderedType = VmTypes.isUnorderedType(allocInfo.getVmType()); boolean forceResourceRefresh = state.hasUnorderedTokens() || unorderedType; /** * GRZE: if the vm type is not "nicely" ordered then we force a refresh of the actual * cluster state. Note: we already hold the cluster gating lock here so this update will * be mutual exclusive wrt both resource allocations and cluster state updates. */ if (forceResourceRefresh) { cluster.refreshResources(); } final List<ResourceToken> tokens = state.requestResourceAllocation(allocInfo, tryAmount, maxAmount); final Iterator<ResourceToken> tokenIterator = tokens.iterator(); try { final Supplier<ResourceToken> allocator = new Supplier<ResourceToken>() { @Override public ResourceToken get() { final ResourceToken ret = tokenIterator.next(); allocInfo.getAllocationTokens().add(ret); return ret; } }; RestrictedTypes.allocateUnitlessResources(tokens.size(), allocator); } finally { // release any tokens that were not allocated Iterators.all( tokenIterator, new Predicate<ResourceToken>() { @Override public boolean apply(final ResourceToken resourceToken) { state.releaseToken(resourceToken); return true; } }); } return allocInfo.getAllocationTokens(); } finally { cluster.getGateLock().readLock().unlock(); } } else { throw new ServiceStateException( "Failed to allocate resources in the zone " + cluster.getPartition() + ", it is currently locked for maintenance."); } }
@Override public boolean apply(Allocation allocInfo) throws MetadataException { BootableImageInfo imageInfo = allocInfo.getBootSet().getMachine(); final ArrayList<BlockDeviceMappingItemType> instanceMappings = allocInfo.getRequest().getBlockDeviceMapping() != null ? allocInfo.getRequest().getBlockDeviceMapping() : new ArrayList<BlockDeviceMappingItemType>(); List<DeviceMapping> imageMappings = new ArrayList<DeviceMapping>(((ImageInfo) imageInfo).getDeviceMappings()); // Figure out the final set of mappings for this instance and add it to the request before // verifying the mappings. // for ( DeviceMapping imageMapping : imageMappings ) { // if( !Iterables.any( instanceMappings, Images.findBlockDeviceMappingItempType( // imageMapping.getDeviceName() ) ) ) { // instanceMappings.add(Images.DeviceMappingDetails.INSTANCE.apply(imageMapping)); // } // } // Is this an overkill?? Should I rather go with the above logic. Damn complexity seems // same... // probably m * n where m is the number of image mappings and n is the number of instance // mappings instanceMappings.addAll( Lists.transform( Lists.newArrayList( Iterables.filter( imageMappings, new Predicate<DeviceMapping>() { @Override public boolean apply(DeviceMapping arg0) { return !Iterables.any( instanceMappings, Images.findBlockDeviceMappingItempType(arg0.getDeviceName())); } })), Images.DeviceMappingDetails.INSTANCE)); if (imageInfo instanceof BlockStorageImageInfo) { // bfebs image if (!instanceMappings.isEmpty()) { // Verify all block device mappings. Dont fuss if both snapshot id and volume size are // left blank Images.isDeviceMappingListValid(instanceMappings, Boolean.TRUE, Boolean.TRUE); BlockStorageImageInfo bfebsImage = (BlockStorageImageInfo) imageInfo; Integer imageSizeGB = (int) (bfebsImage.getImageSizeBytes() / BYTES_PER_GB); Integer userRequestedSizeGB = null; // Find the root block device mapping in the run instance request. Validate it BlockDeviceMappingItemType rootBlockDevice = Iterables.find( instanceMappings, Images.findEbsRootOptionalSnapshot(bfebsImage.getRootDeviceName()), null); if (rootBlockDevice != null) { // Ensure that root device is not mapped to a different snapshot, logical device or // suppressed. // Verify that the root device size is not smaller than the image size if (StringUtils.isNotBlank(rootBlockDevice.getEbs().getSnapshotId()) && !StringUtils.equals( rootBlockDevice.getEbs().getSnapshotId(), bfebsImage.getSnapshotId())) { throw new InvalidMetadataException( "Snapshot ID cannot be modified for the root device. " + "Source snapshot from the image registration will be used for creating the root device"); } else if (StringUtils.isNotBlank(rootBlockDevice.getVirtualName())) { throw new InvalidMetadataException( "Logical type cannot be modified for the root device. " + "Source snapshot from the image registration will be used for creating the root device"); } else if (rootBlockDevice.getNoDevice() != null && rootBlockDevice.getNoDevice()) { throw new InvalidMetadataException( "Root device cannot be suppressed. " + "Source snapshot from the image registration will be used for creating the root device"); } else if ((userRequestedSizeGB = rootBlockDevice.getEbs().getVolumeSize()) != null && userRequestedSizeGB < imageSizeGB) { throw new InvalidMetadataException( "Root device volume cannot be smaller than the image size"); } // Gather all the information for the root device mapping and populate it in the run // instance request if (rootBlockDevice.getEbs().getSnapshotId() == null) { rootBlockDevice.getEbs().setSnapshotId(bfebsImage.getSnapshotId()); } if (rootBlockDevice.getEbs().getVolumeSize() == null) { rootBlockDevice.getEbs().setVolumeSize(imageSizeGB); } if (rootBlockDevice.getEbs().getDeleteOnTermination() == null) { rootBlockDevice.getEbs().setDeleteOnTermination(bfebsImage.getDeleteOnTerminate()); } } else { // This should never happen. Root device mapping will always exist in the block storage // image and or run instance request throw new InvalidMetadataException("Root block device mapping not found\n"); } } } else { // Instance store image // Verify all block device mappings. EBS mappings must be considered invalid since AWS // doesn't support it Images.isDeviceMappingListValid(instanceMappings, Boolean.TRUE, Boolean.FALSE); } // Set the final list of block device mappings in the run instance request (necessary if the // instance mappings were null). Checked with grze that its okay allocInfo.getRequest().setBlockDeviceMapping(instanceMappings); return true; }
@Override public boolean apply(final Allocation allocInfo) throws MetadataException { final UserFullName ownerFullName = allocInfo.getOwnerFullName(); final String instanceProfileArn = allocInfo.getRequest().getIamInstanceProfileArn(); final String instanceProfileName = allocInfo.getRequest().getIamInstanceProfileName(); if (!Strings.isNullOrEmpty(instanceProfileArn) || !Strings.isNullOrEmpty(instanceProfileName)) { final String profileAccount; final String profileName; if (!Strings.isNullOrEmpty(instanceProfileArn)) try { final Ern name = Ern.parse(instanceProfileArn); if (!(name instanceof EuareResourceName)) { throw new InvalidInstanceProfileMetadataException( "Invalid IAM instance profile ARN: " + instanceProfileArn); } profileAccount = name.getAccount(); profileName = ((EuareResourceName) name).getName(); } catch (JSONException e) { throw new InvalidInstanceProfileMetadataException( "Invalid IAM instance profile ARN: " + instanceProfileArn, e); } else { profileAccount = ownerFullName.getAccountNumber(); profileName = instanceProfileName; } final InstanceProfile profile; try { profile = Accounts.lookupInstanceProfileByName(profileAccount, profileName); } catch (AuthException e) { throw new InvalidInstanceProfileMetadataException( "Invalid IAM instance profile: " + profileAccount + "/" + profileName, e); } if (!Strings.isNullOrEmpty(instanceProfileName) && !instanceProfileName.equals(profile.getName())) { throw new InvalidInstanceProfileMetadataException( String.format( "Invalid IAM instance profile name '%s' for ARN: %s", profileName, instanceProfileArn)); } try { final AuthContextSupplier user = allocInfo.getAuthContext(); if (!Permissions.isAuthorized( PolicySpec.VENDOR_IAM, PolicySpec.IAM_RESOURCE_INSTANCE_PROFILE, Accounts.getInstanceProfileFullName(profile), AccountFullName.getInstance(profile.getAccountNumber()), PolicySpec.IAM_LISTINSTANCEPROFILES, user)) { throw new IllegalMetadataAccessException( String.format( "Not authorized to access instance profile with ARN %s for %s", profile.getInstanceProfileArn(), ownerFullName)); } final Role role = profile.getRole(); if (role != null && !Permissions.isAuthorized( PolicySpec.VENDOR_IAM, PolicySpec.IAM_RESOURCE_ROLE, Accounts.getRoleFullName(role), AccountFullName.getInstance(role.getAccountNumber()), PolicySpec.IAM_PASSROLE, user)) { throw new IllegalMetadataAccessException( String.format( "Not authorized to pass role with ARN %s for %s", role.getRoleArn(), ownerFullName)); } if (role != null) { allocInfo.setInstanceProfileArn(profile.getInstanceProfileArn()); allocInfo.setIamInstanceProfileId(profile.getInstanceProfileId()); allocInfo.setIamRoleArn(role.getRoleArn()); } else { throw new InvalidInstanceProfileMetadataException( "Role not found for IAM instance profile ARN: " + profile.getInstanceProfileArn()); } } catch (AuthException e) { throw new MetadataException("IAM instance profile error", e); } } return true; }