<commit_before>﻿using System;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.ComponentModel;
using System.Linq;
using System.Net;
using System.Text;
using System.Threading.Tasks;
using EnumsNET;

namespace StackExchange.Opserver.Data.HAProxy
{
    public static class HAProxyAdmin
    {
        public const string AllServersKey = "*";

        private class ActionPair
        {
            public Proxy Proxy { get; set; }
            public Server Server { get; set; }
        }

        public static Task<bool> PerformProxyActionAsync(IEnumerable<Proxy> proxies, string serverName, Action action)
        {
            var pairs = proxies
                .SelectMany(p => p.Servers
                    .Where(s => s.Name == serverName || serverName.IsNullOrEmpty())
                    .Select(s => new ActionPair {Proxy = p, Server = s})
                ).ToList();

            return PostActionsAsync(pairs, action);
        }

        public static Task<bool> PerformServerActionAsync(string server, Action action)
        {
            var proxies = HAProxyGroup.GetAllProxies();
            var pairs = proxies
                .SelectMany(p => p.Servers
                    .Where(s => s.Name == server)
                    .Select(s => new ActionPair { Proxy = p, Server = s})
                ).ToList();

            return PostActionsAsync(pairs, action);
        }

        public static Task<bool> PerformGroupActionAsync(string group, Action action)
        {
            var haGroup = HAProxyGroup.GetGroup(group);
            if (haGroup == null) return Task.FromResult(false);

            var pairs = haGroup.GetProxies()
                .SelectMany(p => p.Servers
                    .Select(s => new ActionPair {Proxy = p, Server = s})
                ).ToList();

            return PostActionsAsync(pairs, action);
        }

        private static async Task<bool> PostActionsAsync(List<ActionPair> pairs, Action action)
        {
            var instances = new HashSet<HAProxyInstance>();
            var tasks = new List<Task<bool>>(pairs.Count);
            foreach (var p in pairs)
            {
                tasks.Add(PostAction(p.Proxy, p.Server, action));
                instances.Add(p.Proxy.Instance);
            }
            var result = (await Task.WhenAll(tasks).ConfigureAwait(false)).All(r => r);
            // Actions complete, now re-check status
            var instanceTasks = instances.Select(i => i.Proxies.PollAsync(true));
            await Task.WhenAll(instanceTasks).ConfigureAwait(false);
            return result;
        }

        private static async Task<bool> PostAction(Proxy p, Server server, Action action)
        {
            var instance = p.Instance;
            // if we can't issue any commands, bomb out
            if (instance.AdminUser.IsNullOrEmpty() || instance.AdminPassword.IsNullOrEmpty())
            {
                return false;
            }

            // HAProxy will not drain a downed server, do the next best thing: MAINT
            if (action == Action.Drain && server.ProxyServerStatus == ProxyServerStatus.Down)
            {
                action = Action.Maintenance;
            }

            try
            {
                using (var wc = new WebClient())
                {
                    var creds = Convert.ToBase64String(Encoding.ASCII.GetBytes($"{instance.AdminUser}:{instance.AdminPassword}"));
                    wc.Headers[HttpRequestHeader.Authorization] = "Basic " + creds;

                    var responseBytes = await wc.UploadValuesTaskAsync(instance.Url, new NameValueCollection
                    {
                        ["s"] = server.Name,
                        ["action"] = action.AsString(EnumFormat.Description),
                        ["b"] = p.Name
                    }).ConfigureAwait(false);
                    var response = Encoding.UTF8.GetString(responseBytes);
                    return response.StartsWith("HTTP/1.0 303") || response.StartsWith("HTTP/1.1 303");
                }
            }
            catch (Exception e)
            {
                Current.LogException(e);
                return false;
            }
        }
    }

    [Flags]
    public enum Action
    {
        // ReSharper disable InconsistentNaming
        None = 0,
        [Description("ready")] // Set State to READY
        Ready = 1 << 1,<commit_msg>HAProxy errors: log the instance<commit_after>﻿using System;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.ComponentModel;
using System.Linq;
using System.Net;
using System.Text;
using System.Threading.Tasks;
using EnumsNET;

namespace StackExchange.Opserver.Data.HAProxy
{
    public static class HAProxyAdmin
    {
        public const string AllServersKey = "*";

        private class ActionPair
        {
            public Proxy Proxy { get; set; }
            public Server Server { get; set; }
        }

        public static Task<bool> PerformProxyActionAsync(IEnumerable<Proxy> proxies, string serverName, Action action)
        {
            var pairs = proxies
                .SelectMany(p => p.Servers
                    .Where(s => s.Name == serverName || serverName.IsNullOrEmpty())
                    .Select(s => new ActionPair {Proxy = p, Server = s})
                ).ToList();

            return PostActionsAsync(pairs, action);
        }

        public static Task<bool> PerformServerActionAsync(string server, Action action)
        {
            var proxies = HAProxyGroup.GetAllProxies();
            var pairs = proxies
                .SelectMany(p => p.Servers
                    .Where(s => s.Name == server)
                    .Select(s => new ActionPair { Proxy = p, Server = s})
                ).ToList();

            return PostActionsAsync(pairs, action);
        }

        public static Task<bool> PerformGroupActionAsync(string group, Action action)
        {
            var haGroup = HAProxyGroup.GetGroup(group);
            if (haGroup == null) return Task.FromResult(false);

            var pairs = haGroup.GetProxies()
                .SelectMany(p => p.Servers
                    .Select(s => new ActionPair {Proxy = p, Server = s})
                ).ToList();

            return PostActionsAsync(pairs, action);
        }

        private static async Task<bool> PostActionsAsync(List<ActionPair> pairs, Action action)
        {
            var instances = new HashSet<HAProxyInstance>();
            var tasks = new List<Task<bool>>(pairs.Count);
            foreach (var p in pairs)
            {
                tasks.Add(PostAction(p.Proxy, p.Server, action));
                instances.Add(p.Proxy.Instance);
            }
            var result = (await Task.WhenAll(tasks).ConfigureAwait(false)).All(r => r);
            // Actions complete, now re-check status
            var instanceTasks = instances.Select(i => i.Proxies.PollAsync(true));
            await Task.WhenAll(instanceTasks).ConfigureAwait(false);
            return result;
        }

        private static async Task<bool> PostAction(Proxy p, Server server, Action action)
        {
            var instance = p.Instance;
            // if we can't issue any commands, bomb out
            if (instance.AdminUser.IsNullOrEmpty() || instance.AdminPassword.IsNullOrEmpty())
            {
                return false;
            }

            // HAProxy will not drain a downed server, do the next best thing: MAINT
            if (action == Action.Drain && server.ProxyServerStatus == ProxyServerStatus.Down)
            {
                action = Action.Maintenance;
            }

            try
            {
                using (var wc = new WebClient())
                {
                    var creds = Convert.ToBase64String(Encoding.ASCII.GetBytes($"{instance.AdminUser}:{instance.AdminPassword}"));
                    wc.Headers[HttpRequestHeader.Authorization] = "Basic " + creds;

                    var responseBytes = await wc.UploadValuesTaskAsync(instance.Url, new NameValueCollection
                    {
                        ["s"] = server.Name,
                        ["action"] = action.AsString(EnumFormat.Description),
                        ["b"] = p.Name
                    }).ConfigureAwait(false);
                    var response = Encoding.UTF8.GetString(responseBytes);
                    return response.StartsWith("HTTP/1.0 303") || response.StartsWith("HTTP/1.1 303");
                }
            }
            catch (Exception e)
            {
                //if (e is WebException we && we.Response is HttpWebResponse hwr && hwr.StatusCode == HttpStatusCode.Unauthorized) return false;
                Current.LogException(e.AddLoggedData("Instance", instance.Name)
                                      .AddLoggedData("InstanceUrl", instance.Url));
                return false;
            }
        }
    }

    [Flags]
    public enum Action
    {
        // ReSharper disable InconsistentNaming
        None = 0,
        [Description("ready")] // Set State to READY
        Ready = 1 << 1,
{'fraction_non_alphanumeric': 0.09937684486716956, 'fraction_numerical': 0.0034984147808024488, 'mean_word_length': 1.6803398769411075, 'pattern_counts': {'":': 0, '<': 27, '<?xml version=': 0, '>': 43, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 1, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '1548802', 'n_tokens_mistral': 2395, 'n_tokens_neox': 2292, 'n_words': 569}