/* * Copyright 2008-2012 Amazon Technologies, Inc. * * Licensed 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://aws.amazon.com/apache2.0 * * This file 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 com.amazonaws.eclipse.ec2.ui.launchwizard; import java.util.ArrayList; import java.util.List; import org.eclipse.core.runtime.Status; import org.eclipse.jface.viewers.ISelectionChangedListener; import org.eclipse.jface.viewers.SelectionChangedEvent; import org.eclipse.jface.wizard.WizardPage; import org.eclipse.swt.SWT; import org.eclipse.swt.events.ModifyEvent; import org.eclipse.swt.events.ModifyListener; import org.eclipse.swt.events.SelectionEvent; import org.eclipse.swt.events.SelectionListener; import org.eclipse.swt.layout.GridData; import org.eclipse.swt.layout.GridLayout; import org.eclipse.swt.widgets.Combo; import org.eclipse.swt.widgets.Composite; import org.eclipse.swt.widgets.Label; import org.eclipse.swt.widgets.Spinner; import org.eclipse.swt.widgets.Text; import org.eclipse.ui.PlatformUI; import org.eclipse.ui.statushandlers.StatusManager; import com.amazonaws.eclipse.core.AwsToolkitCore; import com.amazonaws.eclipse.ec2.Ec2Plugin; import com.amazonaws.eclipse.ec2.InstanceType; import com.amazonaws.eclipse.ec2.InstanceTypes; import com.amazonaws.eclipse.ec2.ui.ChargeWarningComposite; import com.amazonaws.eclipse.ec2.ui.keypair.KeyPairComposite; import com.amazonaws.eclipse.ec2.ui.keypair.KeyPairSelectionTable; import com.amazonaws.eclipse.ec2.ui.securitygroups.SecurityGroupSelectionComposite; import com.amazonaws.services.ec2.AmazonEC2; import com.amazonaws.services.ec2.model.AvailabilityZone; import com.amazonaws.services.ec2.model.DescribeAvailabilityZonesResult; import com.amazonaws.services.ec2.model.Image; import com.amazonaws.services.identitymanagement.model.InstanceProfile; import com.amazonaws.services.identitymanagement.model.ListInstanceProfilesResult; /** * Wizard Page for launching EC2 instances. */ public class LaunchWizardPage extends WizardPage { private static final String NO_INSTANCE_PROFILE = "None"; private Combo availabilityZoneCombo; private Combo instanceTypeCombo; private Label instanceTypeArchitectureLabel; private Label instanceTypeVirtualCoresLabel; private Label instanceTypeDiskCapacityLabel; private Label instanceTypeMemoryLabel; private Combo instanceProfileCombo; private Text userDataText; private KeyPairComposite keyPairComposite; private SecurityGroupSelectionComposite securityGroupSelectionComposite; private Spinner numberOfHostsSpinner; /** The Image being launched */ private Image image; /** Label displaying the name of the AMI being launched */ private Label amiLabel; /** * Creates a new LaunchWizardPage, configured to launch the specified Image. * * @param image * The Image being launched by this LaunchWizardPage. */ protected LaunchWizardPage(Image image) { super("Launch", "Launch Amazon EC2 Instances", null); this.image = image; this.setDescription("Configure the options for launching your Amazon EC2 instances"); } /** * Loads the controls (key pair selection table, availability zones, * image details) once this page is displayed. * * @see org.eclipse.jface.dialogs.DialogPage#setVisible(boolean) */ @Override public void setVisible(boolean visible) { if (visible) { LaunchWizard launchWizard = (LaunchWizard)this.getWizard(); image = launchWizard.getImageToLaunch(); if (image != null) { amiLabel.setText(image.getImageId() + " (" + image.getArchitecture() + ")"); populateValidInstanceTypes(); } securityGroupSelectionComposite.getRefreshSecurityGroupsAction().run(); keyPairComposite.getKeyPairSelectionTable().refreshKeyPairs(); loadAvailabilityZones(); try { loadInstanceProfiles(); } catch ( Exception e ) { instanceProfileCombo.select(0); AwsToolkitCore.getDefault().logException("Couldn't load IAM Instance Profiles", e); } } super.setVisible(visible); } /* (non-Javadoc) * @see org.eclipse.jface.dialogs.IDialogPage#createControl(org.eclipse.swt.widgets.Composite) */ public void createControl(Composite originalParent) { Composite parent = new Composite(originalParent, SWT.NONE); GridLayout parentLayout = new GridLayout(2, false); parent.setLayout(parentLayout); newLabel(parent, "AMI:"); amiLabel = newLabel(parent, "N/A"); amiLabel.setLayoutData(new GridData(GridData.FILL_HORIZONTAL)); newLabel(parent, "Number of Hosts:"); numberOfHostsSpinner = new Spinner(parent, SWT.BORDER); numberOfHostsSpinner.setSelection(1); numberOfHostsSpinner.setMaximum(20); numberOfHostsSpinner.setMinimum(1); numberOfHostsSpinner.setLayoutData(new GridData(GridData.FILL_HORIZONTAL)); newLabel(parent, "Instance Type:"); instanceTypeCombo = new Combo(parent, SWT.READ_ONLY); instanceTypeCombo.setLayoutData(new GridData(GridData.FILL_HORIZONTAL)); instanceTypeCombo.addModifyListener(new ModifyListener() { public void modifyText(ModifyEvent e) { updateInstanceTypeInformation(); } }); populateValidInstanceTypes(); new Label(parent, SWT.NONE); createInstanceTypeDetailsComposite(parent); // Create the availability zone combo; zones are loaded when the page // becomes visible newLabel(parent, "Availability Zone:"); availabilityZoneCombo = new Combo(parent, SWT.READ_ONLY); availabilityZoneCombo.setLayoutData(new GridData(GridData.FILL_HORIZONTAL)); SelectionListener selectionListener = new SelectionListener() { public void widgetDefaultSelected(SelectionEvent e) {} public void widgetSelected(SelectionEvent e) { updateControls(); } }; newLabel(parent, "Key Pair:"); keyPairComposite = new KeyPairComposite(parent); GridData gridData = new GridData(GridData.FILL_HORIZONTAL); gridData.heightHint = 100; keyPairComposite.setLayoutData(gridData); keyPairComposite.getViewer().addSelectionChangedListener(new ISelectionChangedListener() { public void selectionChanged(SelectionChangedEvent event) { updateControls(); } }); newLabel(parent, "Security Group:"); securityGroupSelectionComposite = new SecurityGroupSelectionComposite(parent); gridData = new GridData(GridData.FILL_HORIZONTAL); gridData.heightHint = 100; securityGroupSelectionComposite.setLayoutData(gridData); securityGroupSelectionComposite.addSelectionListener(selectionListener); newLabel(parent, "Instance Profile:"); instanceProfileCombo = new Combo(parent, SWT.DROP_DOWN | SWT.READ_ONLY); instanceProfileCombo.setLayoutData(new GridData(GridData.FILL_HORIZONTAL)); instanceProfileCombo.setItems(new String[] { NO_INSTANCE_PROFILE }); instanceProfileCombo.select(0); newLabel(parent, "User Data:"); userDataText = new Text(parent, SWT.MULTI | SWT.BORDER); userDataText.setLayoutData(new GridData(GridData.FILL_BOTH)); gridData = new GridData(GridData.FILL_HORIZONTAL); gridData.heightHint = 85; userDataText.setLayoutData(gridData); userDataText.addModifyListener(new ModifyListener() { public void modifyText(ModifyEvent e) { updateControls(); } }); ChargeWarningComposite chargeWarningComposite = new ChargeWarningComposite(parent, SWT.NONE); GridData data = new GridData(GridData.FILL_HORIZONTAL); data.horizontalSpan = 2; data.widthHint = 150; chargeWarningComposite.setLayoutData(data); updateControls(); this.setControl(parent); } /** * Loads the EC2 availability zones for the current region and displays them * in the combo box. */ private void loadAvailabilityZones() { try { availabilityZoneCombo.removeAll(); AmazonEC2 ec2 = Ec2Plugin.getDefault().getDefaultEC2Client(); DescribeAvailabilityZonesResult response = ec2.describeAvailabilityZones(); for ( AvailabilityZone zone : response.getAvailabilityZones() ) { availabilityZoneCombo.add(zone.getZoneName()); availabilityZoneCombo.select(0); } } catch ( Exception e ) { Status status = new Status(Status.WARNING, Ec2Plugin.PLUGIN_ID, "Unable to query EC2 availability zones: " + e.getMessage(), e); StatusManager.getManager().handle(status, StatusManager.LOG); } } private void loadInstanceProfiles() { ListInstanceProfilesResult listInstanceProfiles = AwsToolkitCore.getClientFactory() .getIAMClient().listInstanceProfiles(); List<String> profileNames = new ArrayList<String>(); profileNames.add(NO_INSTANCE_PROFILE); for ( InstanceProfile profile : listInstanceProfiles.getInstanceProfiles() ) { profileNames.add(profile.getInstanceProfileName()); instanceProfileCombo.setData(profile.getInstanceProfileName(), profile); } instanceProfileCombo.setItems(profileNames.toArray(new String[profileNames.size()])); instanceProfileCombo.select(0); } /** * Updates the EC2 instance type combo box so that only the instance types * that are appropriate for the selected Amazon Machine Image being launched * (i.e. the instnace type architecture matches the AMI architecture - 32bit * vs. 64bit). */ private void populateValidInstanceTypes() { instanceTypeCombo.removeAll(); for (InstanceType instanceType : InstanceTypes.getInstanceTypes()) { // Only display instance types that will work with the selected AMI if ( !instanceType.canLaunch(image) ) continue; instanceTypeCombo.add(instanceType.name); instanceTypeCombo.setData(instanceType.name, instanceType); instanceTypeCombo.select(0); } } /* (non-Javadoc) * @see org.eclipse.jface.dialogs.DialogPage#performHelp() */ @Override public void performHelp() { PlatformUI.getWorkbench().getHelpSystem().displayHelp( "com.amazonaws.eclipse.ec2.launchWizardHelp"); } private void updateControls() { setErrorMessage(null); if (keyPairComposite.getKeyPairSelectionTable().isValidKeyPairSelected() == false) { // If an invalid key is selected (as opposed to no key selected) // we want to display an error message. if (keyPairComposite.getKeyPairSelectionTable().getSelectedKeyPair() != null) { setErrorMessage(KeyPairSelectionTable.INVALID_KEYPAIR_MESSAGE); } setPageComplete(false); return; } if (securityGroupSelectionComposite.getSelectedSecurityGroup() == null) { setPageComplete(false); return; } setErrorMessage(null); setPageComplete(true); } private Label newLabel(Composite parent, String text) { Label label; label = new Label(parent, SWT.NONE); label.setText(text); GridData gridData = new GridData(GridData.VERTICAL_ALIGN_BEGINNING); gridData.verticalIndent = 4; label.setLayoutData(gridData); return label; } /* (non-Javadoc) * @see org.eclipse.jface.dialogs.DialogPage#dispose() */ @Override public void dispose() { super.dispose(); keyPairComposite.dispose(); } private void createInstanceTypeDetailsComposite(Composite parent) { Composite composite = new Composite(parent, SWT.NONE); composite.setLayout(new GridLayout(4, true)); composite.setLayoutData(new GridData(GridData.FILL_HORIZONTAL)); Label label = new Label(composite, SWT.NONE); label.setText("Memory:"); instanceTypeMemoryLabel = new Label(composite, SWT.NONE); instanceTypeMemoryLabel.setLayoutData(new GridData(GridData.FILL_BOTH)); label = new Label(composite, SWT.NONE); label.setText("Virtual Cores:"); instanceTypeVirtualCoresLabel = new Label(composite, SWT.NONE); instanceTypeVirtualCoresLabel.setLayoutData(new GridData(GridData.FILL_BOTH)); label = new Label(composite, SWT.NONE); label.setText("Disk Capacity:"); instanceTypeDiskCapacityLabel = new Label(composite, SWT.NONE); instanceTypeDiskCapacityLabel.setLayoutData(new GridData(GridData.FILL_BOTH)); label = new Label(composite, SWT.NONE); label.setText("Architecture:"); instanceTypeArchitectureLabel = new Label(composite, SWT.NONE); instanceTypeArchitectureLabel.setLayoutData(new GridData(GridData.FILL_BOTH)); updateInstanceTypeInformation(); } private void updateInstanceTypeInformation() { // Bail out early if this method has been called before // all the widgets have been initialized if ( instanceTypeCombo == null || instanceTypeArchitectureLabel == null || instanceTypeDiskCapacityLabel == null || instanceTypeMemoryLabel == null || instanceTypeVirtualCoresLabel == null) return; String s = instanceTypeCombo.getText(); InstanceType instanceType = (InstanceType)instanceTypeCombo.getData(s); if (instanceType == null) { instanceTypeArchitectureLabel.setText("N/A"); instanceTypeDiskCapacityLabel.setText("N/A"); instanceTypeMemoryLabel.setText("N/A"); instanceTypeVirtualCoresLabel.setText("N/A"); } else { instanceTypeArchitectureLabel.setText(instanceType.architectureBits + " bits"); instanceTypeDiskCapacityLabel.setText(instanceType.diskSpaceWithUnits); instanceTypeMemoryLabel.setText(instanceType.memoryWithUnits); instanceTypeVirtualCoresLabel.setText(Integer.toString(instanceType.numberOfVirtualCores)); } } /* * Accessors for user entered data */ /** * Returns the selected availability zone. * * @return The selected availability zone. */ public String getAvailabilityZone() { return availabilityZoneCombo.getText(); } /** * Returns the selected security group. * * @return The selected security group. */ public String getSecurityGroup() { return securityGroupSelectionComposite.getSelectedSecurityGroup().getGroupName(); } /** * Returns the String ID of the selected instance type. * * @return The String ID of the selected instance type. */ public String getInstanceTypeId() { String s = instanceTypeCombo.getText(); InstanceType instanceType = (InstanceType)instanceTypeCombo.getData(s); return instanceType.id; } /** * Returns the selected key pair name. * * @return The selected key pair name. */ public String getKeyPairName() { return keyPairComposite.getKeyPairSelectionTable().getSelectedKeyPair().getKeyName(); } /** * Returns the arn of the selected instance profile, or null if none is * selected. */ public String getInstanceProfileArn() { if ( instanceProfileCombo.getSelectionIndex() == 0 ) { return null; } else { return ((InstanceProfile) instanceProfileCombo.getData(instanceProfileCombo.getText())).getArn(); } } /** * Returns the requested user data to pass to instances. * * @return The requested user data to pass to instances. */ public String getUserData() { return userDataText.getText(); } /** * Returns the number of instances requested to be launched. * * @return The number of instances requested to be launched. */ public int getNumberOfInstances() { return numberOfHostsSpinner.getSelection(); } }