Ejemplo n.º 1
0
        /// <summary>
        /// Initializes a new instance of the <see cref="MachineModel"/> class.
        /// </summary>
        /// <param name="model">The object that handles the data storage.</param>
        /// <exception cref="ArgumentNullException">
        ///     Thrown if <paramref name="model"/> is <see langword="null" />.
        /// </exception>
        protected MachineModel(MachineDescription model)
        {
            {
                Lokad.Enforce.Argument(() => model);
            }

            m_Model = model;
        }
Ejemplo n.º 2
0
        private static MachineModel DescriptionToModel(MachineDescription m)
        {
            var physical = m as PhysicalMachineDescription;
            if (physical != null)
            {
                return new PhysicalMachineModel(physical);
            }

            var hyperv = m as HypervMachineDescription;
            if (hyperv != null)
            {
                return new HypervMachineModel(hyperv);
            }

            throw new UnknownMachineTypeException();
        }
        /// <summary>
        /// Updates the stored machine with the data from the changed machine, and indicates if the
        /// stored machine should be patched.
        /// </summary>
        /// <param name="stored">The machine object that was stored in the database.</param>
        /// <param name="changed">The machine object that was changed.</param>
        /// <returns>
        /// A value indicating if the stored value should be patched or not.
        /// </returns>
        private static bool Update(MachineDescription stored, MachineDescription changed)
        {
            stored.Name = changed.Name;
            stored.Description = changed.Description;
            stored.NetworkName = changed.NetworkName;
            stored.MacAddress = changed.MacAddress;
            stored.IsAvailableForTesting = changed.IsAvailableForTesting;

            if (stored.OperatingSystemId != changed.OperatingSystemId)
            {
                stored.OperatingSystem.Machines.Remove(stored);

                stored.OperatingSystemId = changed.OperatingSystemId;
                stored.OperatingSystem = null;

                return true;
            }

            return false;
        }
        /// <summary>
        /// Updates the machine with the data from the given object.
        /// </summary>
        /// <param name="machine">The machine.</param>
        public void Update(MachineDescription machine)
        {
            VerifySchemaVersion();

            var storedMachine = Machine(machine.Id);
            if (storedMachine != null)
            {
                if (!ReferenceEquals(storedMachine, machine))
                {
                    var storedPhysicalMachine = storedMachine as PhysicalMachineDescription;
                    if (storedPhysicalMachine != null)
                    {
                        Update(storedPhysicalMachine, machine as PhysicalMachineDescription);
                    }

                    var storedHypervMachine = storedMachine as HypervMachineDescription;
                    if (storedHypervMachine != null)
                    {
                        Update(storedHypervMachine, machine as HypervMachineDescription);
                    }
                }

                var entry = Entry(storedMachine);
                entry.State = EntityState.Modified;
            }
        }
 /// <summary>
 /// Load the environment.
 /// </summary>
 /// <param name="environment">The specification for the environment.</param>
 /// <param name="pingTimeoutInMilliseconds">The number of milliseconds that the ping operation should wait for a response.</param>
 /// <param name="maximumWaitTimeInMilliseconds">
 ///     The number of milliseconds that the operation should wait for the machine to come online.
 /// </param>
 /// <param name="pingCycleTimeInMilliseconds">The amount of time the operation should wait between consecutive ping operations.</param>
 /// <param name="sectionBuilder">
 /// The object used to write information to the report about the starting and stopping of the environment.
 /// </param>
 /// <returns>An action that should be used to return the environment to its previous state in case the environment load fails.</returns>
 protected abstract Action LoadEnvironment(
     MachineDescription environment,
     int pingTimeoutInMilliseconds,
     int maximumWaitTimeInMilliseconds,
     int pingCycleTimeInMilliseconds,
     ITestSectionBuilder sectionBuilder);
 /// <summary>
 /// Constructs a new active environment proxy.
 /// </summary>
 /// <param name="environment">The specification for the environment.</param>
 /// <param name="preTerminateEnvironment">The action executed just prior to terminating the environment.</param>
 /// <param name="postTerminateEnvironment">The action executed after terminating the environment.</param>
 /// <param name="commands">The object that provides the commands used to communicate with the environment.</param>
 /// <param name="notifications">The object that provides notifications from the environment.</param>
 /// <param name="uploads">The object that tracks the files available for upload.</param>
 /// <param name="sectionBuilder">
 /// The object used to write information to the report about the starting and stopping of the environment.
 /// </param>
 /// <returns>A new active environment proxy object.</returns>
 protected abstract IActiveEnvironment ConstructEnvironmentProxy(
     MachineDescription environment,
     Action preTerminateEnvironment,
     Action postTerminateEnvironment,
     IExecuteTestStepsCommands commands,
     ITestExecutionNotifications notifications,
     IStoreUploads uploads,
     ITestSectionBuilder sectionBuilder);
        /// <summary>
        /// Creates a new active environment based on the given specification.
        /// </summary>
        /// <param name="environment">The specification that provides the configuration for the active environment.</param>
        /// <param name="sectionBuilder">
        /// The object used to write information to the report about the starting and stopping of the environment.
        /// </param>
        /// <param name="onUnload">The action that is executed upon test completion.</param>
        /// <returns>A new active environment.</returns>
        public IActiveEnvironment Load(
            MachineDescription environment,
            ITestSectionBuilder sectionBuilder,
            Action<string> onUnload)
        {
            {
                Lokad.Enforce.Argument(() => environment);
                Lokad.Enforce.Argument(() => onUnload);
            }

            Diagnostics.Log(
                LevelToLog.Trace,
                MachineConstants.LogPrefix,
                string.Format(
                    CultureInfo.InvariantCulture,
                    "Starting environment: {0}",
                    environment.Name));

            var pingTimeout = m_Configuration.HasValueFor(SharedConfigurationKeys.PingTimeoutInMilliseconds)
                ? m_Configuration.Value<int>(SharedConfigurationKeys.PingTimeoutInMilliseconds)
                : GlobalConstants.DefaultPingTimeoutInMilliseconds;

            var signInTimeout = m_Configuration.HasValueFor(SharedConfigurationKeys.MaximumNetworkSignInTimeInMilliseconds)
                ? m_Configuration.Value<int>(SharedConfigurationKeys.MaximumNetworkSignInTimeInMilliseconds)
                : GlobalConstants.DefaultMaximumNetworkSignInTimeInMilliseconds;

            var cycleTime = m_Configuration.HasValueFor(SharedConfigurationKeys.PingCycleTimeInMilliseconds)
                ? m_Configuration.Value<int>(SharedConfigurationKeys.PingCycleTimeInMilliseconds)
                : GlobalConstants.DefaultPingCycleTimeInMilliseconds;
            var emergencyShutdown = LoadEnvironment(environment, pingTimeout, signInTimeout, cycleTime, sectionBuilder);

            var endpoint = FindMachineEndpoint(environment.NetworkName, signInTimeout, cycleTime);
            if (endpoint == null)
            {
                Diagnostics.Log(
                    LevelToLog.Error,
                    MachineConstants.LogPrefix,
                    string.Format(
                        CultureInfo.InvariantCulture,
                        "Could not find remote endpoint on environment: {0}",
                        environment.Name));

                emergencyShutdown();
                throw new CouldNotLoadEnvironmentException();
            }

            Diagnostics.Log(
                LevelToLog.Info,
                MachineConstants.LogPrefix,
                string.Format(
                    CultureInfo.InvariantCulture,
                    "Successfully started {0}",
                    environment.Name));

            var commands = m_Commands.CommandsFor<IExecuteTestStepsCommands>(endpoint);
            var notifications = m_Notifications.NotificationsFor<ITestExecutionNotifications>(endpoint);
            return ConstructEnvironmentProxy(
                environment,
                () => DisconnectCommunication(endpoint),
                () => onUnload(environment.Id),
                commands,
                notifications,
                m_Uploads,
                sectionBuilder);
        }
        private void ShutdownVirtualMachine(MachineDescription environment, ITestSectionBuilder sectionBuilder)
        {
            var specification = environment as HypervMachineDescription;
            if (specification == null)
            {
                throw new InvalidEnvironmentSpecificationException();
            }

            // See if the host machine is alive
            var host = specification.HostMachineId;
            var hostSpecification = m_EnvironmentById(host);
            if (hostSpecification == null)
            {
                sectionBuilder.AddErrorMessage(
                            string.Format(
                                CultureInfo.InvariantCulture,
                                "Could not locate VM host: {0}",
                                hostSpecification.NetworkName));
                sectionBuilder.FinalizeAndStore(false);

                throw new CouldNotLoadEnvironmentException();
            }

            Diagnostics.Log(
                LevelToLog.Debug,
                HyperVConstants.LogPrefix,
                string.Format(
                    CultureInfo.InvariantCulture,
                    "Terminating Hyper-V virtual machine {0} [image: {1}] on host {2}",
                    specification.Name,
                    specification.Image,
                    hostSpecification.Name));

            // Connect to the Hyper-V host for the given environment
            var virtualMachine = new HypervVirtualMachine(specification.Image);
            try
            {
                virtualMachine.Terminate();
            }
            catch (FailedToRestoreEnvironmentException)
            {
                sectionBuilder.AddErrorMessage(
                    string.Format(
                        CultureInfo.InvariantCulture,
                        "Failed to shut down virtual machine: {0}",
                        specification.NetworkName));
                sectionBuilder.FinalizeAndStore(false);

                throw;
            }

            var killTime = DateTimeOffset.Now + TimeSpan.FromMilliseconds(GlobalConstants.DefaultMaximumMachineShutdownTime);
            while ((virtualMachine.State != HypervVirtualMachineState.TurnedOff) && (DateTimeOffset.Now <= killTime))
            {
                Thread.Sleep(10);
            }

            try
            {
                virtualMachine.RestoreToSnapshot(specification.SnapshotToReturnTo);
            }
            catch (FailedToRestoreEnvironmentException)
            {
                sectionBuilder.AddErrorMessage(
                    string.Format(
                        CultureInfo.InvariantCulture,
                        "Failed to restore virtual machine snapshot [{0}] for machine {1}",
                        specification.SnapshotToReturnTo,
                        specification.NetworkName));
                sectionBuilder.FinalizeAndStore(false);

                throw;
            }

            Diagnostics.Log(
                LevelToLog.Debug,
                HyperVConstants.LogPrefix,
                string.Format(
                    CultureInfo.InvariantCulture,
                    "Hyper-V virtual machine {0} terminated",
                    specification.Name));

            sectionBuilder.AddInformationMessage(
                string.Format(
                    CultureInfo.InvariantCulture,
                    "Virtual machine shut down: {0}",
                    specification.NetworkName));
            sectionBuilder.FinalizeAndStore(true);
        }
        /// <summary>
        /// Load the environment.
        /// </summary>
        /// <param name="environment">The specification for the environment.</param>
        /// <param name="pingTimeoutInMilliseconds">The number of milliseconds that the ping operation should wait for a response.</param>
        /// <param name="maximumWaitTimeInMilliseconds">
        ///     The number of milliseconds that the operation should wait for the machine to come online.
        /// </param>
        /// <param name="pingCycleTimeInMilliseconds">The amount of time the operation should wait between consecutive ping operations.</param>
        /// <param name="sectionBuilder">
        /// The object used to write information to the report about the starting and stopping of the environment.
        /// </param>
        /// <returns>An action that should be used to return the environment to its previous state in case the environment load fails.</returns>
        protected override Action LoadEnvironment(
            MachineDescription environment,
            int pingTimeoutInMilliseconds,
            int maximumWaitTimeInMilliseconds,
            int pingCycleTimeInMilliseconds,
            ITestSectionBuilder sectionBuilder)
        {
            var specification = environment as HypervMachineDescription;
            if (specification == null)
            {
                throw new InvalidEnvironmentSpecificationException();
            }

            // See if the host machine is alive
            var host = specification.HostMachineId;
            var hostSpecification = m_EnvironmentById(host);
            if (hostSpecification == null)
            {
                sectionBuilder.AddErrorMessage(
                    string.Format(
                        CultureInfo.InvariantCulture,
                        "Unable to locate host machine: {0}",
                        hostSpecification.NetworkName));
                sectionBuilder.FinalizeAndStore(false);

                throw new CouldNotLoadEnvironmentException();
            }

            // See if the host is awake and if not try to wake it up.
            var physicalHostSpecification = hostSpecification as PhysicalMachineDescription;
            var canStartRemotely = physicalHostSpecification != null && physicalHostSpecification.CanStartRemotely;
            if (!MachineHelpers.WakeNetworkedMachineAndWaitForNetworkSignIn(
                hostSpecification.NetworkName,
                hostSpecification.MacAddress,
                Diagnostics,
                canStartRemotely,
                pingTimeoutInMilliseconds,
                maximumWaitTimeInMilliseconds,
                pingCycleTimeInMilliseconds))
            {
                sectionBuilder.AddErrorMessage(
                    string.Format(
                        CultureInfo.InvariantCulture,
                        "Unable to start host machine: {0}",
                        hostSpecification.NetworkName));
                sectionBuilder.FinalizeAndStore(false);

                throw new CouldNotLoadEnvironmentException();
            }

            // Connect to the Hyper-V host for the given environment
            var virtualMachine = new HypervVirtualMachine(specification.Image);
            if (virtualMachine.State == HypervVirtualMachineState.Paused || virtualMachine.State == HypervVirtualMachineState.Running)
            {
                sectionBuilder.AddWarningMessage(
                    string.Format(
                        CultureInfo.InvariantCulture,
                        "Virtual machine running unexpectedly: {0}",
                        specification.NetworkName));
                sectionBuilder.AddWarningMessage(
                    string.Format(
                        CultureInfo.InvariantCulture,
                        "Resetting virtual machine: {0}",
                        specification.NetworkName));

                KillAndResetVirtualMachine(virtualMachine, specification);
            }

            virtualMachine.Start();

            Diagnostics.Log(
                LevelToLog.Debug,
                HyperVConstants.LogPrefix,
                string.Format(
                    CultureInfo.InvariantCulture,
                    "Started Hyper-V virtual machine {0} [image: {1}] on host {2}",
                    specification.Name,
                    specification.Image,
                    hostSpecification.Name));

            sectionBuilder.AddInformationMessage(
                string.Format(
                    CultureInfo.InvariantCulture,
                    "Started machine: {0}",
                    specification.NetworkName));

            return () =>
            {
                Diagnostics.Log(
                    LevelToLog.Debug,
                    HyperVConstants.LogPrefix,
                    string.Format(
                        CultureInfo.InvariantCulture,
                        "Terminating Hyper-V virtual machine {0} [image: {1}] on host {2}",
                        specification.Name,
                        specification.Image,
                        hostSpecification.Name));

                KillAndResetVirtualMachine(virtualMachine, specification);

                sectionBuilder.AddErrorMessage(
                    string.Format(
                        CultureInfo.InvariantCulture,
                        "Terminated machine: {0}",
                        specification.NetworkName));
                sectionBuilder.FinalizeAndStore(false);
            };
        }
        /// <summary>
        /// Constructs a new active environment proxy.
        /// </summary>
        /// <param name="environment">The specification for the environment.</param>
        /// <param name="preTerminateEnvironment">The action executed just prior to terminating the environment.</param>
        /// <param name="postTerminateEnvironment">The action executed after terminating the environment.</param>
        /// <param name="commands">The object that provides the commands used to communicate with the environment.</param>
        /// <param name="notifications">The object that provides notifications from the environment.</param>
        /// <param name="uploads">The object that tracks the files available for upload.</param>
        /// <param name="sectionBuilder">
        /// The object used to write information to the report about the starting and stopping of the environment.
        /// </param>
        /// <returns>A new active environment proxy object.</returns>
        protected override IActiveEnvironment ConstructEnvironmentProxy(
            MachineDescription environment,
            Action preTerminateEnvironment,
            Action postTerminateEnvironment,
            IExecuteTestStepsCommands commands,
            ITestExecutionNotifications notifications,
            IStoreUploads uploads,
            ITestSectionBuilder sectionBuilder)
        {
            Action shutDownAction =
                () =>
                {
                    preTerminateEnvironment();
                    ShutdownVirtualMachine(environment, sectionBuilder);
                    postTerminateEnvironment();
                };

            return new ActiveHypervEnvironment(
                environment.Id,
                shutDownAction,
                commands,
                notifications,
                uploads);
        }
Ejemplo n.º 11
0
        /// <summary>
        /// Activates the given machine and returns an object that can be used to manipulate
        /// the machine.
        /// </summary>
        /// <param name="currentContext">The current data storage context.</param>
        /// <param name="machine">The description of the machine.</param>
        /// <param name="environment">The description of the environment.</param>
        /// <param name="sectionBuilder">
        ///     The object used to write information to the report about the starting and stopping of the machine.
        /// </param>
        /// <returns>An object that is used to manipulate the active machine.</returns>
        private IActiveEnvironment ActivateMachineForEnvironment(
            IProvideTestingContext currentContext,
            MachineDescription machine,
            TestEnvironment environment,
            ITestSectionBuilder sectionBuilder)
        {
            lock (m_Lock)
            {
                {
                    Lokad.Enforce.Argument(() => machine);
                    Lokad.Enforce.With<ArgumentException>(
                        m_Activators.Any(a => a.EnvironmentTypeToLoad == machine.GetType()),
                        Resources.Exceptions_Messages_NoActivatorHasBeenRegisteredForTheEnvironment);
                }

                var activator = m_Activators.Find(a => a.EnvironmentTypeToLoad == machine.GetType());
                Debug.Assert(activator != null, "We should have found an activator.");

                var activeEnvironment = activator.Load(machine, sectionBuilder, OnEnvironmentUnloaded);

                currentContext.MarkMachineAsActive(machine.Id);
                currentContext.TestEnvironmentSupportedByMachine(environment.Id, machine.Id);

                return activeEnvironment;
            }
        }
        /// <summary>
        /// Load the environment.
        /// </summary>
        /// <param name="environment">The specification for the environment.</param>
        /// <param name="pingTimeoutInMilliseconds">The number of milliseconds that the ping operation should wait for a response.</param>
        /// <param name="maximumWaitTimeInMilliseconds">
        ///     The number of milliseconds that the operation should wait for the machine to come online.
        /// </param>
        /// <param name="pingCycleTimeInMilliseconds">The amount of time the operation should wait between consecutive ping operations.</param>
        /// <param name="sectionBuilder">
        /// The object used to write information to the report about the starting and stopping of the environment.
        /// </param>
        /// <returns>An action that should be used to return the environment to its previous state in case the environment load fails.</returns>
        protected override Action LoadEnvironment(
            MachineDescription environment,
            int pingTimeoutInMilliseconds,
            int maximumWaitTimeInMilliseconds,
            int pingCycleTimeInMilliseconds,
            ITestSectionBuilder sectionBuilder)
        {
            var specification = environment as PhysicalMachineDescription;
            if (specification == null)
            {
                throw new InvalidEnvironmentSpecificationException();
            }

            if (!MachineHelpers.WakeNetworkedMachineAndWaitForNetworkSignIn(
                specification.NetworkName,
                specification.MacAddress,
                Diagnostics,
                specification.CanStartRemotely,
                pingTimeoutInMilliseconds,
                maximumWaitTimeInMilliseconds,
                pingCycleTimeInMilliseconds))
            {
                sectionBuilder.AddErrorMessage(
                    string.Format(
                        CultureInfo.InvariantCulture,
                        "Unable to start machine: {0}",
                        specification.NetworkName));
                sectionBuilder.FinalizeAndStore(false);

                throw new CouldNotLoadEnvironmentException();
            }

            sectionBuilder.AddInformationMessage(
                string.Format(
                    CultureInfo.InvariantCulture,
                    "Started machine: {0}",
                    specification.NetworkName));

            return () =>
            {
                // Do nothing really. We can't shut machines down remotely.
            };
        }
        /// <summary>
        /// Constructs a new active environment proxy.
        /// </summary>
        /// <param name="environment">The specification for the environment.</param>
        /// <param name="preTerminateEnvironment">The action executed just prior to terminating the environment.</param>
        /// <param name="postTerminateEnvironment">The action executed after terminating the environment.</param>
        /// <param name="commands">The object that provides the commands used to communicate with the environment.</param>
        /// <param name="notifications">The object that provides notifications from the environment.</param>
        /// <param name="uploads">The object that tracks the files available for upload.</param>
        /// <param name="sectionBuilder">
        /// The object used to write information to the report about the starting and stopping of the environment.
        /// </param>
        /// <returns>A new active environment proxy object.</returns>
        protected override IActiveEnvironment ConstructEnvironmentProxy(
            MachineDescription environment,
            Action preTerminateEnvironment,
            Action postTerminateEnvironment,
            IExecuteTestStepsCommands commands,
            ITestExecutionNotifications notifications,
            IStoreUploads uploads,
            ITestSectionBuilder sectionBuilder)
        {
            Action shutDownAction =
                () =>
                {
                    preTerminateEnvironment();
                    postTerminateEnvironment();

                    sectionBuilder.AddInformationMessage(
                        string.Format(
                            CultureInfo.InvariantCulture,
                            "Terminated machine: {0}",
                            environment.NetworkName));
                    sectionBuilder.FinalizeAndStore(true);
                };

            return new ActivePhysicalMachineEnvironment(
                environment.Id,
                shutDownAction,
                commands,
                notifications,
                uploads);
        }