public int Invoke(Dictionary<string, string> args)
{
var credentials = new Credentials(args[Arguments.Username], args[Arguments.Password]);
var configId = args.ContainsKey(Arguments.ConfigId) ? args[Arguments.ConfigId] : null;
var vpnId = args.ContainsKey(Arguments.VpnId) ? args[Arguments.VpnId] : null;
var templateId = args[Arguments.TemplateId];
var configName = args[Arguments.ConfigName];
var configState = new ConfigurationState();
var logger = LoggerFactory.GetLogger();
// CreateConfiguration (returns ConfigID of the instantiated template)
SkytapConfiguration newTargetConfig = SkytapApi.CreateConfiguration(credentials, templateId, configName);
// Wait for Skytap to return the expected configuration state. Do this with a retry block
// to test for the desired state every second for 5 minutes.
Retry.Execute(() => SkytapApi.CheckConfigurationForDesiredState(credentials, newTargetConfig.ConfigurationUrl,
new List<string> {ConfigurationStates.Suspended, ConfigurationStates.Stopped}),
NumRetriesCheckConfigState,
_retryIntervalCheckConfigState);
// If VPNID supplied, make a VPN connection, if a configid, create ICNR Connection
if (String.IsNullOrEmpty(vpnId))
{
// CreateIcnrConnection (between the TFSConfigNetwork and the newly instantiated config. Note that it
// is important that the source network ID be the new configuration and the target network ID be
// the (existing) TFS configuration. If the two are reversed, a 409 (conflict) error may occur.
var tfsConfigNetworkId = SkytapApi.GetNetworkIdInConfiguration(credentials, configId);
var icnrId = SkytapApi.CreateIcnrConnection(credentials, newTargetConfig.ConfigurationNetworkId, tfsConfigNetworkId );
newTargetConfig.IcnrId = icnrId;
}
else
{
SkytapApi.AttachVpnConnection(credentials, newTargetConfig.ConfigurationUrl, newTargetConfig.ConfigurationNetworkId, vpnId);
SkytapApi.ConnectVpn(credentials, newTargetConfig.ConfigurationUrl, newTargetConfig.ConfigurationNetworkId, vpnId);
newTargetConfig.VpnId = vpnId;
}
// Need to wait again for ICNR or VPN to complete.
//
// Before starting the configuration, ensure that it is suspended. If it is not suspended (and perhaps stopped)
// wait until it is in the desired state.
var configurationState = SkytapApi.GetConfigurationState(credentials, newTargetConfig.ConfigurationUrl);
if (configurationState != ConfigurationStates.Running)
{
// Attempt to start up the configuration using retry semantics just in case the first request doesn't work. This
// could happen if the configuration tries to restart but the service is busy so the state is returned to
// suspended and we need to retry the start-up.
Retry.Execute(() =>
{
// Start up the configuration by changing its state to running. It is assumed there is a change to the
// runstate at this point, either to "busy" or "running". If the configuration ends up as "running",
// nothing else to do - just continue. If the state goes back to "suspended", need to retry the start
// logic a few more times until the number of retries is exhausted. If it never comes back from "busy" or
// enters some other unknown state, just exit once the retry threshold is reached.
SkytapApi.SetConfigurationState(credentials, newTargetConfig.ConfigurationUrl, ConfigurationStates.Running);
Retry.Execute(() => SkytapApi.CheckConfigurationForDesiredState(credentials, newTargetConfig.ConfigurationUrl,
new List<string> { ConfigurationStates.Running, ConfigurationStates.Suspended }),
NumRetriesCheckConfigState, _retryIntervalCheckConfigState);
// Re-get the configuration state so we can determine whether to give up attempting to start the
// configuration or try again. The exception will trigger a retry.
var currentConfigState = SkytapApi.GetConfigurationState(credentials, newTargetConfig.ConfigurationUrl);
if (currentConfigState == ConfigurationStates.Suspended)
{
throw new ApplicationException(Resources.TfsStartup_UnexpectedReversionToSuspended);
}
},
NumRetriesStartConfig, _retryIntervalStartConfig);
}
// Persist the log file path so that successive invocations of the EXE can use the
// same log file.
newTargetConfig.LogFilePath = ((TraceLogger) logger).LogFilePath;
// Store Config Url so that we can run shutdown on it later since TFS isn't smart enough to do this for us
var configStatePath = configState.Serialize(newTargetConfig);
logger.LogInfo("Persisted configuration path: " + configStatePath);
logger.LogInfo(newTargetConfig.ToString());
return CommandResults.Success;
}