27/01/2014

Ja vs Active Directory

Home

Od niedawna mam okazję pracować, przy pomocy API .NET'owego, z usługami katalogowymi w wydaniu Microsoft'u, czyli z Active Directory. Dla mnie nowa rzecz, więc z zapałem dziecka, które dostało nową zabawkę, zabrałem się do pracy i, jak to często bywa w takich sytuacjach, od razu napotkałem problemy właściwe dla początkujących. W ten sposób dowiedziałem się wielu rzeczy, które dla wyjadaczy mogą być oczywiste, ale dla mnie były nowością. Zebrałem więc je do kupy i tak powstała poniższa lista ku pamięci.

Niektóre z tych rzeczy to drobnostki i jeśli o nich zapomnimy nic wielkiego się nie stanie. Niektóre są jednak bardzo ważne i ich pominięcie może sprowadzić na nas większe kłopoty. Tego rodzaju rzeczy oznaczyłem jako [Ważne]

Projektowanie schematu
  • [Ważne] Przy projektowaniu schematu AD (definicja klas, atrybutów) trzeba być bardzo ostrożnym. AD nie wspiera usuwania klas lub atrybutów, a wiele rzeczy można zdefiniować tylko przy ich tworzeniu, na przykład atrybuty obowiązkowe dla danej klasy. AD wspiera natomiast tzw. dezaktywację. Ma to podobne skutki jak usunięcie, ale i tak wprowadzanie zmian do już używanego schematu AD może być bardzo kłopotliwe.
  • [Ważne] AD to nie C# i nie możesz sobie tak po prostu dodać nowego atrybutu do klasy i oczekiwać, że wszystkie jej instancje zostaną z automatu uaktualnione. Mam tutaj na myśli atrybut mustContain, który określa atrybuty obowiązkowe. Niestety, ale można go podać tylko przy tworzeniu danej klasy. Więcej ograniczeń znajdziecie tutaj.
  • Zamiast atrybutu mustContain można jednak użyć atrybutu mayContain, który określa atrybuty potencjalne.
  • Pisząc skrypt ldif definiujący nowy atrybut nie wystarczy użyć atrybutu attributeSyntax. Potrzebny jest jeszcze atrybut oMSyntax.
Limit 8KB
  • [Ważne] Ponownie AD to nie C#. Obiekty w AD nie mogą mieć więcej niż 8KB, a co za tym idzie, nie mogą zawierać dowolnej liczby atrybutów i wartości. Należy o tym pamiętać szczególnie, jeśli używamy atrybutów wielowartościowych. Ja analizowałem błąd, w którym nie można było dodać nowych wartości do atrybutu właśnie z tego powodu. Przekroczenie limitu 8KB dla danego obiektu skutkuje błędem Administrative limit for this request was exceeded
  • Aby było śmieszniej, nie można łatwo wyznaczyć maksymalnej dopuszczalnej liczby wartości, jakie zmieszczą się w danym atrybucie. Zależy to na przykład od tego, ile atrybutów ma dany obiekt. Ten artykuł zawiera fajną dyskusję praktycznych limitów na liczbę wartości w obiekcie. Sekcja Maximum Database Record Size
Atrybuty powiązane
  • Pewnym rozwiązaniem problemu ograniczonej wielkości obiektów są tzw. atrybuty powiązane (ang. linked attributes), w których zawartość jednego atrybutu zależy od zawartości innych obiektów. Problemem jest natomiast to, że taki wyliczany atrybut musi być typu distinquished name, a więc nie obsłużymy w ten sposób np.: listy liczb całkowitych.
  • [Ważne] Jeśli atrybuty mają być powiązane to muszą być takie od początku.
  • Działanie atrybutów powiązanych może nie być intuicyjne, a więc załączam poglądowy schemat. Jak widać atrybuty połączone to para atrybutów tzw. forward link oraz backward link. Backward link przechowuje wskazania na obiekty, które wskazują go w atrybucie forward link.


Stronicowanie
  • AD ma ograniczenie na liczbę obiektów, jakie można pobrać z bazy w jednym zapytaniu. .NET pozwala to jednak bardzo łatwo obejść dzięki stronicowaniu. Należy ustawić właściwość DirectorySearcher.SizeLimit na 0, a właściwość DirectorySearcher.PageSize na wartość inną niż 0. Nic więcej nie trzeba robić. Więcej szczegółów tutaj.
  • [Ważne] Domyślnie .NET nie używa stronicowania. Jeśli o ty zapomnimy, może dojść do sytuacji, kiedy nasz program nagle przestanie działać, bo nie będzie pobierał wszystkich oczekiwanych obiektów z AD.
    Inne
    • [Ważne] Klasy z przestrzeni nazw System.DirectoryServices, takie jak DirectorySearcher, DirectoryEntry, SearchResultCollection implementują interfejs IDisposable. Piszę o tym ponieważ nawet przykłady na MSDN nie biorą tego pod uwagę!
    • Skrypt ldif, służy do wprowadzania zmian do AD i składa się z sekwencji operacji dodania, usunięcia lub zmodyfikowania czegoś. W przypadku operacji modyfikacji (changetype: add) na końcu powinien znaleźć się znak „-“.
    • Uwaga na spacje w atrybucie cn. Mój skrypt ldif dodający do schematu nowy atrybut wywalił się, bo wartość dla atrybutu cn zawierała na końcu spacje.
    • AD ma różne limity ograniczające, na przykład maksymalna liczbę wartości, jakie można pobrać, maksymalną wielkość strony itp. Pełną listę można znaleźć tutaj.
    Lista ta z pewnością nie jest kompletna. Jeśli macie jakieś sugestie to chętnie ją rozszerzę.

    16/01/2014

    Podsumowania roku są bardzo ważne

    Home

    Refleksja jak powyżej naszła mnie właśnie podczas takiego spotkania podsumowującego. Niby wiedziałem, jakie projekty były realizowane w czasie zeszłego roku, niby wiedziałem o wszystkich przedsięwzięciach podejmowanych w poszczególnych obszarach (innowacja, zarządzanie wiedzą itd.), zdawałem sobie również ze zmian w zespole... Pomimo tego, kiedy zebraliśmy wszystko do kupy byłem zaskoczony, ale w bardzo przyjemny sposób, i podbudowany jak dużo rzeczy udało się zrealizować.

    Niektóre ze zmian były całkiem duże i ewidentnie będą miały pozytywny, długofalowy wpływ. W szczególności mam tutaj na myśli wdrożenie automatycznych testów jednostkowych oraz integracyjnych, wypracowanie zasad ich tworzenia i włączenie ich w oficjalny proces wytwórczy, w czym maczałem swoje palce.

    Mniejszych i większych wydarzeń było sporo więcej i ważne, że wszyscy dowiedzieli się lub przypomnieli sobie, co się działo przez ostatni rok, a przecież nie każdy ma ekspozycję na wszystko co się dzieje. Dlatego uważam, że takie spotkania, o ile zrealizowane dobrze, najlepiej wewnątrz zespołu, tak aby skupić się na konkretach bez corporate bullshit, są bardzo potrzebne.

    Jeśli w Twojej firmie nie ma zwyczaju organizowania takich spotkań, to wyjdź z taką propozycją albo jeśli masz taką możliwość zorganizuj je samemu.

    13/01/2014

    Brakujące konto MSSQLSERVER

    Home

    Na swoim lokalnym komputerze pliki baz danych trzymam na innym dysku niż systemowy. Raz, że jest to dysk systemowy i chcę go w razie czego w dowolnym momencie sformatować, a dwa, że jest to dysk SSD i na nadmiar miejsca nie narzekam. Na super wydajności mi natomiast nie zależy. Utworzyłem więc katalog db na innym dysku, bazy przestawiłem w tryb offline, skopiowałem pliki mdf oraz ldf i uaktualniłem ścieżki przy pomocy komendy ALTER DATABASE. Na koniec chciałem przełączyć bazy w tryb online, ale MSSQL krzyknął, że nie ma uprawnień do plików mdf/ldf.

    Spojrzałem więc jak skonfigurowany jest domyślny katalog używanego przez MSSQL do przechowywania plików baz danych. Okazało się, że właścicielem jest niejaki MSSQLSERVER. Swój katalog db chciałem więc skonfigurować w ten sam sposób. Głupia sprawa ale wyglądało, że takie konto nie istnieje w systemie! Spojrzałem jeszcze raz na domyślny katalog i wszystko się zgadzało. Przejrzałem listę wszystkich kont i nie znalazłem tam nic przypominającego konto MSSQLSERVER.

    Znalezienie rozwiązania zajęło mi trochę czasu i w końcu okazało się, że pełna nazwa konta to NTSERVICE\MSSQLSERVER. Aby było łatwiej jest ono niewidoczne w okienku Select Users or Groups ale jeśli wpiszemy je w pole Enter the object names to select: to zostanie znalezione.

    08/01/2014

    Czy zadajesz pytania?

    Home

    Czy próbowaliście kiedy rozwiązać następujące zadanie?

    Napisz program, który wypisze na ekran konsoli swój własny kod. Możesz pominąć białe znaki.

    (01-09-2014) Dodatkowe wymaganie:
    Kod programu nie powinien być wczytany z pliku, bazy danych lub innego nośnika.

    Zachęcam do sprawdzenia swoich sił. Zadanie to wysłałem również swoim kolegom z pracy. Wcześniej rozwiązałem je samemu i w gruncie rzeczy spodziewałem się podobnych do mojego rozwiązań. Zostałem jednak zaskoczony, bo okazało się, że inni podeszli do tego problemu troszkę inaczej, uzyskując ten sam wynik co ja, a nawet lepszy, bo w prostszy sposób. Sytuacja ta przypomniała mi kilka innych, w których zadanie prostego pytania:

    Co o tym myślisz? Jak byś zabrał się do tego zadania?

    Pomogło mi rozwiązać problem szybciej, lepiej, sprawniej... W pracy programisty niezwykle ważne jest konfrontowanie swoich pomysłów z rozwiązaniami innych. Wydaje Ci się, że wszystko zrobiłeś dobrze? A może ślęczysz nad jakimś problem już bardzo długo i cały czas nie możesz znaleźć zadowalającego rozwiązania?

    Zawsze warto zapytać kolegi\koleżanki siedzącej obok o zdanie. To nie kosztuje dużo, a bardzo się opłaca. Nie ma głupich pytań chyba, że jak to powiedział mi kiedyś kumpel chcesz zapytać czy jak staniesz na torach i chwycisz się trakcji to pojedziesz jak tramwaj :)

    05/01/2014

    Jeszcze więcej szczegółów na temat IntelliTrace

    Home

    O IntelliTrace pisałem już wielokrotnie. Do tej pory nie wyjaśniłem jednak, że chociaż IntelliTrace nazywamy debugger'em historycznym to w rzeczywistości IntelliTrace jest profilerem. Dokładniej mówiąc jednym z komponentów składowych IntellITrace jest niezarządzana implementacja interfejsu ICorProfiler. Profiler ten komunikuje się z zarządzaną częścią IntelliTrace, czyli z programem IntellITrace.exe. IntellITrace.exe jest natomiast używane przez Visual Studio.

    Ma to ciekawe skutki. Oznacza bowiem, że oprócz nagrywania działania aplikacji z poziomu Visual Studio albo bezpośrednio przy pomocy programu IntelliTrace.exe (jak to opisałem tutaj) dochodzi jeszcze jedna opcja. Otóż możemy skorzystać ze zmiennych środowiskowych COR_ENABLE_PROFILING oraz COR_PROFILER i monitorować przy pomocy IntelliTrace zarządzaną usługę systemową.

    W tym celu znajdujemy w rejestrze klucz:

    HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\services\<Nazwa_Usługi>

    Dodajemy do niego nową wartość Environment o typie REG_MULTI_SZ i zawartości:
    COR_PROFILER={b19f184a-cc62-4137-9a6f-af0f91730165}
    COR_ENABLE_PROFILING=1
    VSLOGGER_CPLAN=COLLECTION_PLAN_PATH
    
    W pierwszeh linijce wskazujemy profiller, jaki ma zostać użyty do monitorowania usługi. W tym przypadku będzie to IntelliTrace dla VS 2012. Identyfikator profiler'a IntelliTrace dla VS 2010 jest inny tj. 301EC75B-AD5A-459C-A4C4-911C878FA196. Oczywiście, jeśli nie mamy zainstalowanej danej wersji Visual Studio, to profiler nie będzie zarejestrowany w systemie.

    W drugiej linijce po prostu włączamy profilowanie, a w trzeciej wskazujemy ścieżkę to pliku XML z konfiguracją IntelliTrace. O tym, skąd wziąć ten plik, pisałem we wspomnianym już artykule oraz w innych postach z serii o IntelliTrace. Tutaj zaznaczę tylko, że należy w nim ustawić nazwę pliku z logiem oraz katalog roboczy np.:
    <CollectionPlan xmlns="urn:schemas-microsoft-com:visualstudio:tracelog">
      <StartupInfo>
        <LogFileName>log.itrace</LogFileName>
        <LogFileDirectory>c:\Logs</LogFileDirectory>
        <MaximumLogFileSize>-1</MaximumLogFileSize>
      </StartupInfo>
      ...
    </CollectionPlan>
    
    Na koniec po prostu uruchamiamy naszą usługę, a kiedy wykona swoje zadanie zatrzymujemy i przeglądamy nagrany log na przykład w Visual Studio.

    Niestety, ale to podejście nie zadziała dla zwykłych aplikacji uruchamianych z dwukliku. Sądzę, że w takich wypadkach IntelliTrace nie obsługuje zmiennej środowiskowej VSLOGGER_CPLAN, ale tego akurat nie jestem pewny. Istnieje jednak inna możliwość. W praktyce używana jest rzadko, gdyż jest mało wygodna, ale pokazuje jak IntelliTrace działa od środka. A więc uruchamiamy wiersz polecenia i wpisujemy następujące komendy:
    rem Uruchamiamy instancję IntelliTrace o nazwie 'test' ale nie wskazujemy programu do monitorowania
    %INTELLI_TRACE_PATH%\IntelliTrace.exe start /n:test /f:"D:\WorkingDir\IntelliTrace\IntelliTraceWorkingDir\Test.iTrace" /cp:%COLLECTION_PLAN%
    
    rem Włączamy profilowanie
    set COR_ENABLE_PROFILING=1
    
    rem Ustawiamy profiler, który chcemy użyć do monitorowania naszego programu tj. IntelliTrace
    set COR_PROFILER={b19f184a-cc62-4137-9a6f-af0f91730165}
    
    rem Ustawiamy nazwę instancji IntelliTrace z jakiej chcemy skorzystać
    set VSLOGGERNAME=test
    
    rem Uruchamiamy program, który chcemy monitorować przy pomocy instancji IntelliTrace o nazwie 'test'
    MyProgram.exe
    
    rem Zamykamy instancję IntelliTrace o nazwie 'test'
    %INTELLI_TRACE_PATH%\IntelliTrace.exe stop /n:test /cp:%COLLECTION_PLAN%
    
    Podejście to różni się od standardowego tym, że tutaj instancja IntelliTrace czeka na uruchomienie programu, zamiast uruchomić go samemu.