public AssetClusterModule() { Get["/cluster"] = x => { var syncedMachines = ClusterConfiguration.GetNodes(); var config = ClusterConfiguration.GetClusterInfo(); foreach (var node in syncedMachines) { var services = _api.Get <List <RssdpServiceModel> >(node.ModelUrl + "device/services"); node.Services = services; } var importPortMapping = syncedMachines .Select(_ => _.Services) .Merge() .Select(_ => _.Name) .ToHashSet() .Select(_ => new Cluster.PortMapping { ServiceName = _, ServicePort = "", VirtualPort = "" }) .ToList() ; var existingPortMapping = config.PortMapping.ToList(); foreach (var i in importPortMapping) { if (existingPortMapping.FirstOrDefault(_ => _.ServiceName == i.ServiceName) == null) { existingPortMapping.Add(i); } } config.PortMapping = existingPortMapping.ToList(); var model = new PageAssetClusterModel { Info = config, ClusterNodes = syncedMachines.OrderBy(_ => _.Hostname).ThenBy(_ => _.PublicIp).ToList(), NetworkAdapters = IPv4.GetAllLocalDescription().ToList() }; return(JsonConvert.SerializeObject(model)); }; Post["/cluster/save"] = x => { string config = Request.Form.Config; string ip = Request.Form.Ip; var model = JsonConvert.DeserializeObject <List <NodeModel> >(config); var model2 = JsonConvert.DeserializeObject <Cluster.Configuration>(ip); ClusterConfiguration.SaveNodes(model); ClusterConfiguration.SaveConfiguration(model2); new Do().ClusterChanges(); DeployClusterConfiguration(); return(HttpStatusCode.OK); }; Post["Accept Configuration", "/cluster/accept"] = x => { string file = Request.Form.File; string content = Request.Form.Content; if (string.IsNullOrEmpty(file)) { return(HttpStatusCode.BadRequest); } if (string.IsNullOrEmpty(content)) { return(HttpStatusCode.BadRequest); } ConsoleLogger.Log($"[cluster] received config for file: {file}"); DirectoryWatcherCluster.Stop(); try { FileWithAcl.WriteAllText(file, content, "644", "root", "wheel"); } catch (Exception) { ConsoleLogger.Warn(""); DirectoryWatcherCluster.Start(); return(HttpStatusCode.InternalServerError); } DirectoryWatcherCluster.Start(); var dict = Dicts.DirsAndServices; if (dict.ContainsKey(file)) { ConsoleLogger.Log("[cluster] restart service bind to config file"); var services = dict[file]; foreach (var svc in services) { Systemctl.Enable(svc); Systemctl.Restart(svc); } } ConsoleLogger.Log("[cluster] apply changes after new config"); new Do().HostChanges(); new Do().NetworkChanges(); DeployClusterConfiguration(); return(HttpStatusCode.OK); }; #region [ Handshake + cluster init ] Post["/asset/handshake/start", true] = async(x, ct) => { string conf = Request.Form.HostJson; var remoteNode = JsonConvert.DeserializeObject <NodeModel>(conf); if (remoteNode == null) { return(HttpStatusCode.InternalServerError); } const string pathToPrivateKey = "/root/.ssh/id_rsa"; const string pathToPublicKey = "/root/.ssh/id_rsa.pub"; if (!File.Exists(pathToPublicKey)) { var k = Bash.Execute($"ssh-keygen -t rsa -N '' -f {pathToPrivateKey}"); ConsoleLogger.Log(k); } var key = File.ReadAllText(pathToPublicKey); if (string.IsNullOrEmpty(key)) { return(HttpStatusCode.InternalServerError); } var dict = new Dictionary <string, string> { { "ApplePie", key } }; var r = new ApiConsumer().Post($"{remoteNode.ModelUrl}asset/handshake", dict); if (r != HttpStatusCode.OK) { return(HttpStatusCode.InternalServerError); } //ho fatto l'handshake, quindi il nodo richiesto è pronto per essere integrato nel cluster //1. controllo la configurazione var clusterConfiguration = ClusterConfiguration.GetClusterInfo(); if (string.IsNullOrEmpty(clusterConfiguration.Guid)) { clusterConfiguration.Guid = Guid.NewGuid().ToString(); } if (string.IsNullOrEmpty(clusterConfiguration.Priority)) { clusterConfiguration.Priority = "100"; } if (string.IsNullOrEmpty(clusterConfiguration.NetworkInterface)) { clusterConfiguration.NetworkInterface = ""; } if (string.IsNullOrEmpty(clusterConfiguration.VirtualIpAddress)) { clusterConfiguration.VirtualIpAddress = ""; } //2. salvo la configurazione ClusterConfiguration.SaveConfiguration(clusterConfiguration); //3. controllo i nodi presenti nella configurazione var clusterNodes = ClusterConfiguration.GetNodes(); var iplocals = IPv4.GetAllLocalAddress().ToList(); var disc = await ServiceDiscovery.Rssdp.Discover(); //4. per prima cosa controllo l'host locale var localNode = disc.FirstOrDefault(_ => iplocals.Contains(_.PublicIp)); //5. se non c'è lo aggiungo if (clusterNodes.FirstOrDefault(_ => _.MachineUid == localNode.MachineUid && _.PublicIp == localNode.PublicIp) == null) { clusterNodes.Add(localNode); } //7. se non c'è lo aggiungo if (clusterNodes.FirstOrDefault(_ => _.MachineUid == remoteNode.MachineUid && _.PublicIp == remoteNode.PublicIp) == null) { clusterNodes.Add(remoteNode); } //8. salvo la configurazione dei nodi ClusterConfiguration.SaveNodes(clusterNodes); //9. riavvio/avvio il servizio di cluster new Do().ClusterChanges(); DeployClusterConfiguration(); return(HttpStatusCode.OK); }; Post["/asset/handshake"] = x => { string apple = Request.Form.ApplePie; var info = apple.Split(new[] { " " }, StringSplitOptions.RemoveEmptyEntries); if (info.Length < 2) { return(HttpStatusCode.InternalServerError); } var key = info[0]; var remoteUser = info[1]; const string user = "******"; var model = new AuthorizedKeyModel { RemoteUser = remoteUser, User = user, KeyValue = key }; var authorizedKeysConfiguration = new AuthorizedKeysConfiguration(); authorizedKeysConfiguration.AddKey(model); try { DirectoryWithAcl.CreateDirectory("/root/.ssh"); const string authorizedKeysPath = "/root/.ssh/authorized_keys"; if (File.Exists(authorizedKeysPath)) { var f = File.ReadAllText(authorizedKeysPath); if (!f.Contains(apple)) { FileWithAcl.AppendAllLines(authorizedKeysPath, new List <string> { apple }, "644", "root", "wheel"); } } else { FileWithAcl.WriteAllLines(authorizedKeysPath, new List <string> { apple }, "644", "root", "wheel"); } Bash.Execute($"chmod 600 {authorizedKeysPath}", false); Bash.Execute($"chown {user}:{user} {authorizedKeysPath}", false); return(HttpStatusCode.OK); } catch (Exception ex) { ConsoleLogger.Log(ex); return(HttpStatusCode.InternalServerError); } }; Post["/cluster/deploy"] = x => { var clusterConfiguration = Request.Form.Cluster; Cluster.DeployConf model = Newtonsoft.Json.JsonConvert.DeserializeObject <Cluster.DeployConf>(clusterConfiguration); ClusterConfiguration.SaveConfiguration(model.Configuration); ClusterConfiguration.SaveNodes(model.Nodes); new Do().ClusterChanges(); return(HttpStatusCode.OK); }; //Post["/asset/wol"] = x => { // string mac = Request.Form.MacAddress; // CommandLauncher.Launch("wol", new Dictionary<string, string> { { "$mac", mac } }); // return HttpStatusCode.OK; //}; //Get["/asset/nmasp/{ip}"] = x => { // string ip = x.ip; // var result = CommandLauncher.Launch("nmap-ip-fast", new Dictionary<string, string> { { "$ip", ip } }).Where(_ => !_.Contains("MAC Address")).Skip(5).Reverse().Skip(1).Reverse(); // var list = new List<NmapScanStatus>(); // foreach(var r in result) { // var a = r.SplitToList(" ").ToArray(); // var mo = new NmapScanStatus { // Protocol = a[0], // Status = a[1], // Type = a[2] // }; // list.Add(mo); // } // list = list.OrderBy(_ => _.Protocol).ToList(); // return JsonConvert.SerializeObject(list); //}; #endregion }