Platforma do zarządzania kompami w sieci Lan

0

Witam, mam takie pytanie. Czy jest ktoś wstanie poratować informacjami na temat korzystnych rozwiązań oraz o jakie biblioteki czy frameworki można się oprzeć tworząc oprogramowanie do pełnego zarządzania komputerami w sieci zamkniętej LAN.

Cel: Właśnie został wdrożony nowy projekt, który zastąpił pracowników Botami. Program komunikuje się z specjalną stworzoną przez nas platformową. Problem jaki mamy jest to iż komputery pracują na modemach 3G i są często restartowane by uzyskać nowe IP co sprawia poważne problemy w sytuacji gdy chcemy się zdalnie zalogować na komputer włączyć aktualizacje, czy je po prostu zrestartować.

Ogólnie jest dużo programów, do zarządzania jednak też je chcemy zautomatyzować ;) Dla tego chcemy postawić własną platformę.

Na czym nam zależy, na początku.

  • Mieć możliwość włączenia wybranego lub całej grupy komputerów
  • Mieć możliwość restartowania komputerów

Myślę że to jest trywialne i można się oprzeć na wywoływaniu skryptów. Przydała by się możliwość manipulacji plikami oraz odpalaniu procesów exe wraz z ich podglądem.

Myślę, że w tym przypadku musiał bym jeszcze wystawić klienta ;)

Oraz możliwość do pobierania informacji czy komputer pracuje, oraz czy ma dostęp do internetu zewnętrznego.

2 pytanie czy miał ktoś odczynienia z modemami 3G lub potrafił by mi pomóc z tą klasą, przyznam się szczerze wiozłem gotowca, przez dłuższy czas działało lecz z starszymi modemami zaczęło wybijać iż Callback::Invoke jest przeciążony, sytuacja zdarza się raz na 1000, czyli raz na 2 dni na jednej maszynie, przy starych modemach i słabym zasięgu.

Lecz w sytuacji gdy chcemy kompy chcemy postawić a Niemczech i Anglii muszę załatać jakoś tą dziurę

 
public class RasManager
    {

        public const int RAS_MaxEntryName = 256;
        public const int RAS_MaxPhoneNumber = 128;
        public const int UNLEN = 256;
        public const int PWLEN = 256;
        public const int DNLEN = 15;
        public const int MAX_PATH = 260;
        public const int RAS_MaxDeviceType = 16;
        public const int RAS_MaxCallbackNumber = RAS_MaxPhoneNumber;

        public delegate void Callback(uint unMsg, int rasconnstate, int dwError);

        [StructLayout(LayoutKind.Sequential, Pack = 4, CharSet = CharSet.Auto)]
        public struct RASDIALPARAMS
        {
            public int dwSize;
            [MarshalAs(UnmanagedType.ByValTStr, SizeConst = RAS_MaxEntryName + 1)]
            public string szEntryName;
            [MarshalAs(UnmanagedType.ByValTStr, SizeConst = RAS_MaxPhoneNumber + 1)]
            public string szPhoneNumber;
            [MarshalAs(UnmanagedType.ByValTStr, SizeConst = RAS_MaxCallbackNumber + 1)]
            public string szCallbackNumber;
            [MarshalAs(UnmanagedType.ByValTStr, SizeConst = UNLEN + 1)]
            public string szUserName;
            [MarshalAs(UnmanagedType.ByValTStr, SizeConst = PWLEN + 1)]
            public string szPassword;
            [MarshalAs(UnmanagedType.ByValTStr, SizeConst = DNLEN + 1)]
            public string szDomain;
            public int dwSubEntry;
            public int dwCallbackId;
        }

        [DllImport("rasapi32.dll", CharSet = CharSet.Auto)]
        public static extern int RasDial(int lpRasDialExtensions, string
        lpszPhonebook,
        ref RASDIALPARAMS lprasdialparams, int dwNotifierType,
        Callback lpvNotifier, ref int lphRasConn);

        private RASDIALPARAMS RasDialParams;
        private int Connection;

        public RasManager()
        {
            Connection = 0;
            RasDialParams = new RASDIALPARAMS();
            RasDialParams.dwSize = Marshal.SizeOf(RasDialParams);
        }

        #region Properties 
        public string UserName
        {
            get
            {
                return RasDialParams.szUserName;
            }
            set
            {
                RasDialParams.szUserName = value;
            }
        }

        public string Password
        {
            get
            {
                return RasDialParams.szPassword;
            }
            set
            {
                RasDialParams.szPassword = value;
            }
        }

        public string EntryName
        {
            get
            {
                return RasDialParams.szEntryName;
            }
            set
            {
                RasDialParams.szEntryName = value;
            }
        }
        #endregion

        public int Connect()
        {
            Callback rasDialFunc = new Callback(RasManager.RasDialFunc);
            RasDialParams.szEntryName += "\0";
            RasDialParams.szUserName += "\0";
            RasDialParams.szPassword += "\0";
            int result = RasDial(0, null, ref RasDialParams, 0, rasDialFunc, ref
            Connection);
            GC.Collect();
            return result;
            
        }

        public static void RasDialFunc(uint unMsg, int rasconnstate, int dwError)
        {
        }
    }
1

"Lecz w sytuacji gdy chcemy kompy chcemy postawić a Niemczech i Anglii muszę załatać jakoś tą dziurę"

no to przeciez nie bedzie juz to siec LAN. Zdecyduj sie czy ma byc to LAN czy WAN z po prostu jakims LANem...

Tak czy inaczej jezeli siec lokalna to na kazdym komputerze klienckim napisalbym prosty skrypt ktory nasluchuje na danym porcie UDP. Jezeli go zlapie to wysyla z powrotem swoje IP i dzieki temu serwer moze polaczyc sie poprzez TCP. Gdy masz polaczenie TCP to mozesz juz robic co chcesz (wyslesz jakas komende i na jej podstawie niech cos klient wykonuje)

Jezeli bedzie to WAN z jakims LANem to proponuje uzyc Message Queue. Np RabbitMQ

0

Chcemy zrobić to w taki sposób iż jeden komputer Będzie matką M1 : M1 ma być podłączona do sieci zamkniętej LAN i jej zadaniem ma być kontrola całej sieci. M1 - ma mieć stałe IP by nie mieć problemów do połączenia. W sieci lan ma być podłączonych z 30 Botów A1 - A30.
Wszystkie Automaty do swojej pracy wykorzystują modemy 3G z dużymi pulami IP. Mniej więcej zmiana IP jest raz max 2 minuty. W tym czasie program cały czas pracuje. Są sytuacje gdy np jak program ma iść w stan spoczynku np na 30 min. Program się usypia oraz rozłącza się z internetem, w celu bezpieczeństwa.

Dla tego musimy np zarządzać nimi z całkiem innego poziomu. Przynajmniej ja mam takie zdanie.

0

oba rozwiazania ktore przedstawilem sa nie zalezne od adresu IP

RabbitMQ uzywa czegos podobnego to co ja opisalem. Ja opisalem po prostu najprotsza wersje. RabbitMQ dodaje do tego jeszcze calkiem duzo zabezpieczen i jest szybki.

Jezeli zrobisz polaczenie TCP to niech serwer wysyla heartbeat co jakis czas. Jezeli komputer nieodpowie to zamknie martwe polaczenie. UDP niech wysyla caly czas wiec jezlei jakis klient zresetuje ip czy pojawi sie nagle online to bedzie mogl ten pakiet odebrac.

Jezeli chcesz sprawdzic co to za komputer to gdy polaczysz sie poprzez TCP to mozesz do serwera wyslac dane co to za komputer (normalnie przez socket)

0

dzięki za rozwiązania. A jeszcze takie pytanie

public delegate void Callback

masz może pomysł dla czego się to wysypuje, raz udało mi się to uchwycić na testach. Chodzi o przeciążenie kolekcji. Jakby mi się to zdarzało częściej to próbami bym jakoś wyeliminował. Lecz ostatnio spędziłem ponad 2 godziny restartując internet ponad 200 razy i nic. Puszczając na produkcji wywaliło po 5 min pracy i potem całą noc na kompie mi działało do rana.

2

Użyj loggera (np. nlog) i zaloguj do pliku i/lub maila zaadresowanego do siebie całą treść wyjątku razem ze stosem i parametrami wywołania, to będziesz wiedział dlaczego się cokolwiek sypie.

0

Poprawiłem funkcje już się nie wysypuje przeciążenie delegata przy słabszych modemach

 
public class RasManager
    {

        public const int RAS_MaxEntryName = 256;
        public const int RAS_MaxPhoneNumber = 128;
        public const int UNLEN = 256;
        public const int PWLEN = 256;
        public const int DNLEN = 15;
        public const int MAX_PATH = 260;
        public const int RAS_MaxDeviceType = 16;
        public const int RAS_MaxCallbackNumber = RAS_MaxPhoneNumber;

        public delegate void Callback(uint unMsg, int rasconnstate, int dwError); // delegat

        [StructLayout(LayoutKind.Sequential, Pack = 4, CharSet = CharSet.Auto)]
        public struct RASDIALPARAMS
        {
            public int dwSize;
            [MarshalAs(UnmanagedType.ByValTStr, SizeConst = RAS_MaxEntryName + 1)]
            public string szEntryName;
            [MarshalAs(UnmanagedType.ByValTStr, SizeConst = RAS_MaxPhoneNumber + 1)]
            public string szPhoneNumber;
            [MarshalAs(UnmanagedType.ByValTStr, SizeConst = RAS_MaxCallbackNumber + 1)]
            public string szCallbackNumber;
            [MarshalAs(UnmanagedType.ByValTStr, SizeConst = UNLEN + 1)]
            public string szUserName;
            [MarshalAs(UnmanagedType.ByValTStr, SizeConst = PWLEN + 1)]
            public string szPassword;
            [MarshalAs(UnmanagedType.ByValTStr, SizeConst = DNLEN + 1)]
            public string szDomain;
            public int dwSubEntry;
            public int dwCallbackId;
        }

        [DllImport("rasapi32.dll", CharSet = CharSet.Auto)]
        public static extern int RasDial(int lpRasDialExtensions, string
        lpszPhonebook,
        ref RASDIALPARAMS lprasdialparams, int dwNotifierType,
        Callback lpvNotifier, ref int lphRasConn); // invoka 

        private RASDIALPARAMS RasDialParams;
        private int Connection;

        public RasManager()
        {
            Connection = 0;
            RasDialParams = new RASDIALPARAMS();
            RasDialParams.dwSize = Marshal.SizeOf(RasDialParams);
        }

        #region Properties 
        public string UserName
        {
            get
            {
                return RasDialParams.szUserName;
            }
            set
            {
                RasDialParams.szUserName = value;
            }
        }

        public string Password
        {
            get
            {
                return RasDialParams.szPassword;
            }
            set
            {
                RasDialParams.szPassword = value;
            }
        }

        public string EntryName
        {
            get
            {
                return RasDialParams.szEntryName;
            }
            set
            {
                RasDialParams.szEntryName = value;
            }
        }
        #endregion
        private static readonly Callback rasDialFunc = RasManager.RasDialFunc;

        public int Connect()
        {
           
            RasDialParams.szEntryName += "\0";
            RasDialParams.szUserName += "\0";
            RasDialParams.szPassword += "\0";
            
            int result = RasDial(0, null, ref RasDialParams, 0, rasDialFunc, ref
            Connection);
            GC.Collect();
            return result;

        }



        public static void RasDialFunc(uint unMsg, int rasconnstate, int dwError)
        {
        }
    }
1

I tak loguj wszystkie błędy i zapewnij sobie ich wysyłanie, bo jak się okaże, że pierdyknął komputer na drugim końcu świata i to po raz drugi z nieznanego, ale być może tego samego powodu, to ktoś się może wkurzyć.

Co do GC.Collect(); - wołanie GC po imieniu i nazwisku to takie faux pas programistyczne. GC zwykle i tak oleje sugestię posprzątania.
Jeśli którakolwiek z klas używanych przez Ciebie implementuje IDisposable, to sam też powinieneś zaimplementować ten interfejs, w metodzie Dispose wywołać wszystkie metody Dispose używanych klas, a w miejscu, w którym tworzona jest instancja Twojej klasy musisz zadbać o to samo, najlepiej poprzez tworzenie obiektów w klauzuli using. To akurat tutaj nie będzie miało miejsca, tak na przyszłość Cię informuję.

0

Tylko że w tym przypadku mam problem z uchwyceniem ;) tego błędu. Nie wiem jak to wytłumaczyć, nigdzie się w kodzie nic nie sypie tylko debuger mnie informuje że coś zostało tam przeciążone. Nie wiem jak takie błędy uchwycić. Bo żaden exception nie wybija.

Ogólnie wprowadziłem jeszcze dodatkową aplikację wskrzeszającą cały system w 10 min. Nie miałem możliwości naszczacie jej jeszcze przetestować na produkcji ^^ lecz na testach się sprawowała.

Dodatkowo będziemy wprowadzali KVM z możliwością zdalnego zalogowania się do konsoli i manualnych operacji już na konkretnych systemach.

a wracając do twojej rady, myślę że też coś takie wprowadzę pod koniec ;) jak już faktycznie będziemy w takie fazie iż dostęp będzie możliwy tylko zdalny. Obecnie projekt zaczyna powili wstawać z trybu raczkowania xD

0

Nie "coś tam zostało przeciążone", było skopiować dokładną treść błędu i poszukać rozwiązania w sieci. Nigdy nie lekceważ treści wyjątków/błędów/komunikatów.

1

Coś tam napisałem, bo nie chciało mi się szukać komunikatu przeciążony był Callback::Invoke i system nie wiedział kiedy może zwolnić pamięć. Szukałem w sieci i znalazłem podobny problem oraz sugerowane rozwiązania. Na podstawie tego artykułu rozwiązanie zaimplementowałem u mnie. Jednak tamta funkcja tyczyła się całkiem czegoś innego.

1 użytkowników online, w tym zalogowanych: 0, gości: 1