Пример #1
0
        public VirtualMachine(
            string subscriptionId,
            IVirtualMachine virtualMachine,
            ILogger logger,
            IEnumerable <Setting> settings,
            NotificationDelay notificationDelay,
            ResourceGroup resourceGroup,
            LocationTZ timezone,
            DateTime timeNowUtc
            )
        {
            this.subscriptionId    = subscriptionId;
            this.virtualMachine    = virtualMachine;
            this.logger            = logger;
            this.settings          = settings;
            this.notificationDelay = notificationDelay;
            this.resourceGroup     = resourceGroup;
            this.timezones         = timezone.GetAll();
            this.timeNowUtc        = timeNowUtc;

            // Using the settings, set the start and stop time for the vm
            startTime = settings.First(s => s.name == "vm_start").value;
            stopTime  = settings.First(s => s.name == "vm_stop").value;

            SetPowerState();
        }
Пример #2
0
        /// <summary>
        /// Determine if the group has expired and therefore if it should be deleted
        /// Uses the duration in days from settings to determine if the RG should be deleted
        /// All calculations are performed in UTC
        /// </summary>
        /// <returns></returns>
        public bool ShouldDelete()
        {
            bool result = false;

            // Do not delete if the group is flagged as being InUse
            if (!InUse())
            {
                TimeZoneInfo zone = TimeZoneInfo.Utc;

                // Get the destroy mode
                bool destroy = Convert.ToBoolean(settings.First(s => s.name == "destroy").value);

                // Get the timezones
                IEnumerable <LocationTZ> timezones = timezone.GetAll();

                // Determine if the group contains the date tag
                bool hasTagDate = HasTag("tag_date");

                if (hasTagDate)
                {
                    string tagDate = GetTag("tag_date");

                    // Get the created date of the rg in UTC
                    DateTime rgCreateDateUtc = DateTime.SpecifyKind(DateTime.Parse((string)tagDate), DateTimeKind.Utc);

                    // Determine the expiry date
                    Double rgLifetimeDuration = Convert.ToDouble(settings.First(s => s.name == "duration").value);
                    expiryDate = rgCreateDateUtc.AddDays(rgLifetimeDuration);

                    // compare the expiry date against the time now to determine if it is expired
                    if (timeNowUtc > expiryDate)
                    {
                        result = true && destroy;

                        // set the message to display in the slack notification
                        if (destroy)
                        {
                            message = "Your resource group has expired and will be deleted";
                            level   = "danger";
                        }
                        else
                        {
                            message = "Your resource group has expired and would be delete. Reaper is not running in destructive mode";
                            level   = "warning";
                        }
                    }
                }
            }

            return(result);
        }
Пример #3
0
        public async Task <bool> Process(DocumentClient client, ILogger log, string id = null)
        {
            logger = log;

            bool   error       = false;
            Uri    webhook_url = null;
            string pretext     = null;

            // Create a list of resource group candiates that may be deleted
            List <string> resource_group_candidates = new List <string>();

            // Define a variable to accept the response from SlackClient
            HttpResponseMessage response = new HttpResponseMessage();

            // Get the required settings
            IEntity setting = new Setting(client, log);
            IEnumerable <Setting> settings = setting.GetAllByCategory(new string[] { "tags", "slack", "lifecycle" });

            bool destroy    = Convert.ToBoolean(settings.First(s => s.name == "destroy").value);
            bool manage_vms = Convert.ToBoolean(settings.First(s => s.name == "manage_vms").value);

            // Create a connection to Slack
            // ensure that the webhook url can be turned into a URI
            try
            {
                webhook_url = new Uri(settings.First(s => s.name == "webhook_url").value);
            }
            catch
            {
                string msg = (String.Format("Unable to create a URI from `webhook_url`: {0}", settings.First(s => s.name == "webhook_url").value));
                log.LogInformation(msg);
                error = true;
            }

            if (!error)
            {
                SlackClient slackClient = new SlackClient(
                    webhook_url,
                    settings.First(s => s.name == "bot_username").value,
                    settings.First(s => s.name == "icon_url").value,
                    settings.First(s => s.name == "token").value,
                    log,
                    Convert.ToBoolean(settings.First(s => s.name == "slack_enabled").value)
                    );

                // Get all the subscriptions that have the reaper enabled on them
                IEntity subscription = new Subscription(client, log);
                // dynamic subscriptions = subscription.Get(subscription);
                IEnumerable <Subscription> subscriptions = subscription.GetUsingSQL("SELECT * FROM subscriptions t WHERE t.enabled = true");

                // Get al the timezones
                LocationTZ timezone = new LocationTZ(client, log);
                IEnumerable <LocationTZ> timezones = timezone.GetAll();

                // Define timestamp to work against so it is the same time for all checks
                DateTime timeNowUtc = DateTime.UtcNow;

                // Create a NotificationDelay object to use to
                // get previoud notification alerts and add new ones
                NotificationDelay notificationDelay = new NotificationDelay(client, log);

                // iterate around the subscriptions and check the resource groups and vms in each
                foreach (Subscription sub in subscriptions)
                {
                    // Determine the timenow to be used as criteria
                    // This is done on each subscription because there might be a lot of groups to enumerate
                    DateTime _time_now = DateTime.UtcNow;
                    string   time_now  = _time_now.ToString("yyyy-MM-ddTHH:mm:ssZ");

                    AzureCredentials credentials = new AzureCredentials(
                        new ServicePrincipalLoginInformation
                    {
                        ClientId     = sub.client_id,
                        ClientSecret = sub.client_secret
                    }, sub.tenant_id, AzureEnvironment.AzureGlobalCloud
                        );
                    Microsoft.Azure.Management.Fluent.Azure azure = Microsoft.Azure.Management.Fluent.Azure.Configure().Authenticate(credentials).WithSubscription(sub.subscription_id);

                    // get a list of resource groups in the subscription
                    IEnumerable <IResourceGroup> resource_groups = azure.ResourceGroups.List();

                    // iterate around the resource groups
                    foreach (IResourceGroup resource_group in resource_groups)
                    {
                        pretext = null;

                        // if the id is not null check the name and skip if the current is not the right one
                        if (!String.IsNullOrEmpty(id))
                        {
                            if (resource_group.Name != id)
                            {
                                continue;
                            }
                        }

                        ResourceGroup rg = new ResourceGroup(
                            resource_group,
                            log,
                            settings,
                            notificationDelay,
                            sub.subscription_id,
                            timezone,
                            timeNowUtc
                            );

                        log.LogInformation("action=reaper, resource_group={resourceGroup}, message=Considering group", resource_group.Name);

                        // Determine if the resource group has expired or not
                        bool notify  = rg.ShouldNotify();
                        bool expired = rg.ShouldDelete();

                        // if notify is set, send a slack message
                        if (notify)
                        {
                            // Attempt to get the slack userid for the email address
                            await slackClient.GetUserIdByEmail(rg.emailAddress);

                            // Set the properties of the slack message
                            slackClient.AddField("Name", resource_group.Name);
                            slackClient.AddField("Expiry Date", rg.expiryDate.ToString("yyyy-MM-ddTHH:mm:ss"));

                            if (rg.HasTag("tag_alert"))
                            {
                                pretext = "Notification triggered as Resource Group has been tagged with the Alert tag";
                            }

                            // Add the message to the slack client
                            slackClient.AddAttachmentItem(rg.message, rg.level, pretext);

                            // Send the slack message
                            response = await slackClient.SendMessageAsync();

                            // Set a record in the notification delay that this resource group has had
                            // a notification sent out
                            notificationDelay.Update(sub.subscription_id, resource_group.Name, "resourceGroup");
                        }

                        // If the group has expired, delete it
                        // Otherwise work out what machines need to be powered on or off
                        if (expired)
                        {
                            azure.ResourceGroups.DeleteByNameAsync(resource_group.Name);
                        }
                        else
                        {
                            // So that the Slack user is not inundated with messages, ensure that all machines that have been
                            // found to be in violation of running times are bundled up
                            List <string> stopped = new List <string>();
                            List <string> started = new List <string>();

                            // as the resource group is valid look at the virtual machines that are running
                            // and determine if they should be shutdown or not
                            IEnumerable <IVirtualMachine> vms = azure.VirtualMachines.ListByResourceGroup(resource_group.Name);

                            foreach (IVirtualMachine vm in vms)
                            {
                                // create a virtualMachine object to work with
                                VirtualMachine virtualMachine = new VirtualMachine(
                                    sub.subscription_id,
                                    vm,
                                    log,
                                    settings,
                                    notificationDelay,
                                    rg,
                                    timezone,
                                    timeNowUtc
                                    );

                                // Set the power state of the machine
                                VirtualMachine.Status powerStatus = virtualMachine.SetPowerState();

                                // Using the powerStatus determine what has been started and what has been stopped
                                if (powerStatus == VirtualMachine.Status.Started && !virtualMachine.NotifiedWithinTimePeriod())
                                {
                                    started.Add(virtualMachine.GetName());
                                }

                                if (powerStatus == VirtualMachine.Status.Stopped && !virtualMachine.NotifiedWithinTimePeriod())
                                {
                                    stopped.Add(virtualMachine.GetName());
                                }

                                // Add a notification for this machine
                                notificationDelay.Update(sub.subscription_id, resource_group.Name, "virtualMachine", virtualMachine.GetName());
                            }

                            // only send out messages for VMs if there are some
                            if (vms.Count() > 0 && (started.Count > 0 || stopped.Count > 0) && notify)
                            {
                                // Define the properties of the slack message to send
                                slackClient.AddField("Name", resource_group.Name);
                                slackClient.AddField("VMs Started", String.Join("\n", started));
                                slackClient.AddField("VMs Stopped", String.Join("\n", stopped));

                                // Define the message that needs to be attached
                                string message = "The following machines have been stopped or started.";
                                if (!manage_vms)
                                {
                                    message += " Reaper is not currently running in active mode, no machines have been stopped or started.";
                                }

                                slackClient.AddAttachmentItem(message, "warning");

                                // Send a slack message to the owner
                                response = await slackClient.SendMessageAsync();
                            }
                        }
                    }
                }
            }

            return(true);
        }