OS2MO Data Import

Magentas officielle repo til integrationer og eksportfunktioner til OS2MO.

For spørgsmål til koden eller brug af den, er man velkommen til at kontakte Magenta ApS <info@magenta.dk>

Integrationer

Integration til SD Løn

Indledning

Denne integration gør det muligt at hente og opdatere organisations- og medarbejderoplysninger fra SD Løn til OS2MO.

Opsætning

For at kunne afvikle integrationen, kræves loginoplysninger til SD-Løn, som angives via settings.json, desuden anvendes en række felter som angiver den lokale anvendelse af SD Løn. De påkrævede felter er:

  • integrations.SD_Lon.institution_identifier: Institution Identifer i SD.
  • integrations.SD_Lon.sd_user: Brugernavn (inklusiv foranstillet SY) til SD.
  • integrations.SD_Lon.sd_password: Password til SD.
  • integrations.SD_Lon.base_url: url til SD’s webinterface.
  • integrations.SD_Lon.global_from_date: Virkningsdato for import på formen YYYY-MM-DD.
  • integrations.SD_Lon.import.too_deep: Liste over SD niveauer som anses som afdelingsniveau.
  • integrations.SD_Lon.monthly_hourly_divide: Skilleværdi for måneds/timelønnede.
  • integrations.SD_Lon.job_function: Feltet kan have en af to vædier: EmploymentName eller JobPositionIdentifier, se yderligere nedenfor.

Desuden kan disse ikke-påkrævede felter angives:

  • integrations.SD_Lon.employment_field: Angiver et af MOs ekstrafelter på engagementer, hvis feltet angives vil integrationen skrive værdien af EmploymentName i dette felt.
  • integrations.SD_Lon.skip_employment_types: En liste over værdier af JobPositionIdentifier som ikke skal importeres. Hvis et engagement har en type fra listen, vil engagementet bliver ignoreret og ikke importeret i MO. Den tilhørende bruger vil dog blive oprettet, men vil optræde uden engagementer (med mindre personen har andre engagementer i kommunen).
  • integrations.SD_Lon.no_salary_minimum_id: Angiver en minimum påkrævet job position id for ulønnede medarbejdere. Alle ulønnede medarbejder med et id under dette minimum får aldrig deres engagement oprettet i MO.

Hvis integrations.SD_Lon.job_function har værdien EmploymentName vil ansættelsers stillingsbetegnelser bliver taget fra SDs felt af samme navn, som er et fritekstfelt. Integrationen vil oprette en klasse for alle forekommende stillingsbetegnelser. Benyttes i stedet værdien JobPositionIdentifier vil stillingsbetegelsen blive taget fra dette felt i SD, som er et klassicieret felt.

Desuden er det nødvendigt at angive adressen på MO og LoRa i variablerne:

  • mox.base
  • mora.base

Brug af integrationen

De forskellige underprogrammer kan alle tilgåes igennem ét hoved program, nemlig sd_cli, ved kørsel af dette program vises underprogrammerne, og deres parametre og formål kan udforskes. Kør blot: ` python integrations/SD_Lon/sd_cli.py --help `

Detaljer om importen

Udtræk fra SD Løn foregår som udgangspunkt via disse webservices:

  • GetOrganization20111201
  • GetDepartment20111201
  • GetPerson20111201
  • GetEmployment20111201

Det er desuden muligt at køre et udtræk som synkroniserer ændringer som er meldt ind til SD Løn, men endnu ikke har nået sin virkningsdato:

  • GetEmploymentChanged20111201
  • GetPersonChangedAtDate20111201

Endelig er der også en implementering af løbende synkronisering af ændringer i SD Løn, til dette anvendes udover de nævne webservices også:

  • GetEmploymentChangedAtDate20111201

Hvis der ønskes synkronisering af titler hørende til JobPositionIdentifier anvendes desuden:

  • GetProfession20080201

Alle enheder fra SD importeres 1:1 som de er i SD, dog er det muligt at flytte enheder uden hverken overenhed eller underenheder til en særlig overenhed kaldet ‘Forældreløse enheder’.

Medarbejdere som er ansat på niveauerne, angivet i konfigurationensnøglen integrations.SD_Lon.import.too_deep rykkes op til det laveste niveau højere end dette, og der oprettes en tilknytning til den afdeling de befinder sig i i SD Løn.

Det er muligt at levere en ekstern liste med ledere, eller alternativt at benytte SD Løns JobPositionIdentifier til at vurdere at en medarbejder skal regnes som leder.

Medarbejdere i statuskode 3 regnes for at være på orlov.

Der importeres ingen adresser på medarbejdere. Disse kan eventuelt hentes fra ad-integrationen.

Alle personer og ansættelser som kan returneres fra de ovennævnte webservices importeres, både passive og aktive. Dette skyldes dels et ønske om et så komplet datasæt som muligt, dels at SDs vedligeholdsesservices gå ud fra, at alle kendte engagementer er i den lokale model.

Den importerede startdato for engagementer er desværre ikke i alle tilfælde korrekt, men repræsenterer for aktive ansættelser den dato hvor den nuværende ansættelsesstatus indtrådte, da det ikke er muligt at finde den korrekte oprindelige startdato uden et meget stort antal kald mod SDs api. For afsluttede ansættelser vil sidste ændrede status være lig med slutdatoen, i disse tilfælde anvendes i stedet SDs felt EmploymentDate, som desværre er et fritekstfelt som i pricippet kan være behæftet med fejl.

Postadresser på enheder hentes fa SD og valideres mod DAR. Hvis adressen kan entydigt genkendes hos DAR, gemmes den tilhørende DAR-uuid på enheden i MO.

Email adresser og p-numre importeres fra SD hvis disse findes for enheden.

Vi importerer UUID’er på enheder fra SD til MO så enheder i MO og SD har samme UUID.

Medarbejdere har ikke en UUID i SD, så her benyttes cpr som nøgle på personen og ansættelsesnummeret som nøgle på engagementer. Brugerens UUID i MO vil enten blive tilfældigt valgt, eller trukket fra eksternt givet liste som matcher cpr-numre med ønskede UUID’er i MO. Denne funktionalitet kan anvendes til at sikre, at brugere ikke skifter UUID hvis det bliver nødvendigt at genimporere fra SD. TIl hjælp til dette findes et script (cpr_uuid.py) under exports som kan lave en sådan liste fra en kørende instans af MO.

Engagementstyper

Alle medarbejdere som har et ansættelsesnummer udelukkende med tal, tildeles en af to ansættelsestyper:

  • Medarbejder (månedsløn), hvis ansættelsesnummeret er lavere end værdien angivet i nøglen integrations.SD_Lon.monthly_hourly_divide.
  • Medarbejder (timeløn), hvis ansættelsesnummeret er højere.

Hvis medarbejderen har et ansættelsesnummer, som ikke udelukkende er tal, vil ansættelsestypen blive bestemt fra personens JobPositionIdentifier, hvor der i MO er oprettet klasser der svarer til disse værdier. Den tilknyttede tekst til hver klasse kan sættes med et hjælpeværktøj (beskrevet nedenfor).

Primær ansættelse

SD Løn har ikke et koncept om primæransættelse, men da AD integrationen til MO har behov for at kunne genkende den primære ansættelse til synkronisering, bestemmes dette ud fra en beregning:

En medarbejders primære ansættelse regnes som den ansættelse som har den største arbejdstidsprocent, hvis flere har den samme, vælges ansættelsen med det laveste ansættelsenummer. Hvis en ansættelse er manuelt angivet til at være primær, vil denne ansættelse altid regnes som primær.

Ansættelser i SDs statuskode 0 kan anses som primære hvis ingen andre ansættelser er primære (altså, medarbejderen har udelukkende ansættelser i statuskode 0). Hvis en medarbejder har ansættelser i både status 0 og status 1, vil en ansættelse i status 1 blive beregnet til primær og status 0 ansættelsen vil ikke blive betragtet som primær.

Informationen om primæransætelse opretholdes i MOs facet primary_type, som ved import af SD altid populeres med disse fire klasser:

  • Manuelt primær ansættelse: Dette felt angiver at en ansættelse manuelt er sat til at være primær
  • Ansat: Angiver en medarbejders beregnede primære ansættelse.
  • Ansat - Ikke i løn: Angiver SD Løns statuskode 0. Hvis ingen andre primære ansætelser findes vil denne type regnes som primær.
  • Ikke-primær ansat: Angiver alle andre ansættelser for en medarbejder.

Manuelt primær optræder ikke direkte i imports, men kan sættes manuelt fra MOs GUI. De øvrige primærklasser håndteres af SD integrationen, og må ikke sættes manuelt.

En medarbejder skifter ikke ansættelsestype selvom vedkommende fratræder sit engagement. En ansættelses aktuelle status angives i stedet via MOs start- og slutdato. Er slutdato’en i fortiden, er vedkommende ikke længere ansat og vil i MOs gui fremgå i fanen fortid. Er en medarbejers startdato i fremtiden, er personen endnu ikke tiltrådt, og fremgår i fanen fremtid. .. _Håndtering af enheder:

Håndtering af enheder

SDs API til udlæsning af organisationsenheder er desværre meget mangelfuldt, og integrationen har derfor en yderst primitiv håndtering af enheder:

Ved førstegangsimport vil alle aktuelle enheder blive importeret med den virkningstid som oplyses af kald til GetDepartment. Dette er dog ikke nødvendigvis den egentlige oprettelsesdato for enheden og der vil være tilfælde hvor startdato er enten for tidlig eller for sen i forhold til den reele startdato for enheden.

Der findes ikke nogen differentiel service fra SD som oplyser om ændringer i organisationen, og der sker derfor som udgangspunkt ingen synkronisering af enhedstræet mellem SD og MO. I de tilfælde hvor der ansættes en medarbejder i en enhed som enten ikke eksisterer i MO, eller hvor enhedens virkningstid er kortere end ansættelsens start, vil MO oprette enheden eller forlænge dens virkningstid så den bliver i stand til at rumme engagementet.

Da det er meget vanskeligt at hente historisk information om enheder, vil MO oprette eller rette enheden med udgangspunkt i de data som gælder for enheden på importdagen. Enheden vil herefter fremgå af MO som om den altid har haft det navn og den placering den har på importdagen.

Hvis en enhed omdøbes eller flyttes i SD, vil denne ændring ikke fremgå af MO, med mindre der foretages en manuel synkronisering, dette kan gøres ved at at afvikle scriptet fix_departments.py, hvis kommunen ønsker det, er det muligt at slå en funktionalitet til som tillader denne afvikling via en knap i MOs front-end.

Når fix_departments.py afvikles på en enhed, vil enheden og dens forældres navne og hierakiske placering blive hentet fra SD og den nye tilstand vil blive skrevet til MO med evig virkning både bagud og fremad i tid. Hvis enhedens niveau er angivet i integrations.SD_Lon.import.too_deep til at være et afdelingsnieau vil integrationen desuden genberegne placeringen de engagementer som SD har registreret på enheden som vil blive flyttet opad til det laveste strukturniveau i undertræet. Denne flytning vil få en registreret virkningstid som er lig med den dag fix_departments.py blev afviklet.

Det skal altså understreges, at MOs historiske information om enhder ikke er retvisende. Det betyder dels, at det ikke er muligt at se tidligere navne på enheden, men mere bemærkelsesværdigt er det, at det ikke er muligt at se tidligere placeringer i organisationshierakiet. Det betyder altså, at enheden potentielt tidligere kan have været placeret et helt andet sted i organisationen. Hvis en medarbejder har været ansat i en enhed mens enheden er er blevet flyttet, vil dette ikke fremgå at medarbejderens fortidsfane, da engagementets tilknytning til enheden ikke har været ændret. Det er derfor vigtigt at holde sig for øje, at selvom en medarbejders historik ikke indeholder ændringer i organisatorisk placering, kan vedkommende godt være flyttet alligevel i form af eventuelle flytninger af hele enheden.

I tilknytning til SD importen, er der i øjeblikket ved at blive implementeret en funktionalitet som via SD Løns beskedservice kan oprette enheder i SD når de oprettes i MO. Med denne service vil den fremadrettede historik for enheder fra idriftsættelsen af servicen, blive korrekt.

Hjælpeværktøjer

Udover de direkte værktøjer til import og løbende opdateringer, findes et antal hjælpeværktøjer:

  • test_sd_connectivity.py: Et lille værktøj som tester at den lokale settings.json indeholder de nødvendige nøgler. Desuden tester programmet for en række potentielle fejl, eksempevis om felterne har gyldige værdier og om det er muligt at kontakte SD Løn med de angivne brugeroplysinger.
  • test_mo_against_sd.py: Et værktøj som tester udvalgte personers engagementer mod SD løn of checker at MO og SD er løn har samme opfattelse af om personens engagementer er aktive eller ej. Værktøjet kan anvendes på et enkelt person eller på alle personer som har ansættelse i en bestemt enhed (alle engagementer for disse personer vil blive tjekket også dem i andre enheder). Værktøjet anvender opslag til SDs API’er og kan derfor kun anvendes i begrænset omfang, og af samme årsag er der ikke implementeret mulighed for at tjekke alle ansatte.
  • calculate_primary.py: Et værktøj som er i stand til at gennemløbe alle ansættelser i MO og afgøre om der for alle medarbejdere til alle tider findes et primærengagement. Værktøjet er også i stand til at reparere en (eller alle) ansættelser hvor dette ikke skulle være tilfældet. Dette modul importeres desuden af koden til løbende opdatering, hvor den bruges til at genberegne primæransættelser når der skær ændringer i en medarbejders ansættelsesforhold. Værktøjet er udstyret med et kommandolinjeinterface, som kan udskrive en liste over brugere uden primærengagement (eller med mere end et) samt opdatere primære engagementer for en enkelt bruger eller for alle brugere.
  • sync_job_id.py: Dette værktøj kan opdatere den tekst som vises i forbindelse med ansættelsestyper og stillingsbetegnelser som er knyttet til SDs JobPositionIdentifier. Efter den initielle import vil klassens navn modsvare talværdien i SD, og dette værktøj kan efterfølgende anvendes til at enten at synkronisere teksten til den aktuelle værdi i SD eller til en valgfri tekst.
  • fix_departments.py: En implementering af logikken beskrevet under afsnitet Håndtering af enheder. Udover anvendelsen i den løbende integrationen, indeholder programmet også et kommandolinjeværktøj som kan anvendes til manuelt at fremprovokere en synkronisering af en enhed (med tilhørende overenheder) til den nuværende tilsand af SD Løn. Hvis værktøjet afvikles på en enhed som anses for at være Afdelings-niveau, vil det opdatere alle enhedens ansættelser, så engagementerne flyttes til de korrekte NY-niveauer (som kan være ændret, hvis afdelingen er flyttet).
  • sd_fix_organisation.py: Tidligere forsøg på at håndtere opdateringer af enheder. Scriptet findes nu kun som basis for evenutelle senere forsøg på at lave et fuldt historisk import af enhedstræet.

Tjekliste for fuldt import

Overordnet foregår opstart af en ny SD import efter dette mønster:

  1. Kør importværktøjet med fuld historik (dette er standard opførsel).
  2. Kør en indledende ChangedAt for at hente alle kendte fremtidige ændringer og intitialisere den lokale database over kørsler.
  3. Kør sd_changed_at.py periodisk (eksempelvis dagligt).
  4. Eventuelt synkronisering af stillingsbetegnelser.
  5. Eventuelt synkronisering fra AD.

1. Kør importværktøjet

En indledende import køres ved at oprette en instans af ImportHelper ImportHelper

importer = ImportHelper(
    create_defaults=True,
    mox_base=MOX_BASE,
    mora_base=MORA_BASE,
    store_integration_data=False,
    seperate_names=True
)

Hverken importen eller efterfølgende synkronisering med ChangedAt anvender integrationsdata, og det er derfor valgfrit om vil anvende dette.

Importen kan derefter køres med disse trin:

sd = sd_importer.SdImport(
    importer,
    ad_info=None,
    manager_rows=None
)

sd.create_ou_tree(
    create_orphan_container=False,
    sub_tree=None,
    super_unit=None
)
sd.create_employees()

importer.import_all()

Hvor der i dette tilfælde ikke angives ledere eller en AD integration. Disse to punkter diskuteres under punkterne Ledere i SD Løn og AD Integration til SD Import.

Parametren sub_tree kan angives med en uuid og det vil så fald kun blive undertræet med den pågældende uuid i SD som vil blive importeret. Det er i øjeblikket et krav, at dette træ er på rod-niveau i SD.

Importen vil nu blive afviklet og nogle timer senere vil MO være populeret med værdierne fra SD Løn som de ser ud dags dato.

2. Kør en indledende ChangedAt

I SD Løn importeres i udgangspunktet kun nuværende og forhenværende medarbejdere og engagementer, fremtidige ændringer skal hentes i en seperat process. Denne process håndteres af programmet sd_changed_at.py (som også anvendes til efterfølgende daglige synkroniseringer). Programmet tager i øjeblikket desværre ikke mod parametre fra kommandolinjen, men har brug for at blive rettet direkte i koden, hvor parametren init i __main__ delen af programmet skal sættes til True.

Programet kan nu afvikles direkte fra kommandolinjen

python3 sd_changed_at.py

Herefter vil alle kendte fremtidige virkninger blive indlæst til MO. Desuden vil der blive oprettet en sqlite database med en oversigt over kørsler af changed_at (se ChangedAt.db) .

3. Kør sd_changed_at.py periodisk

Daglige indlæsninger foregår som nævnt også med programmet sd_changed_at.py, hvilket foregår ved at sætte init til False og køre programmet uden yderligere parametre. Programmet vil så spørge ChangedAt.db om hvorår der sidst blev synkroniseret, og vil herefter synkronisere yderligere en dag frem i tiden.

4. Eventuelt synkroisering af stillingsbetegnelser

Hvis nøglen * integrations.SD_Lon.job_function er valgt til JobPositionIdentifier, vil alle stillingsbetegnelser nu være talværdier fra SD Løns klassificerede stillinger, for at få læsbare stillinger skal disse synkroniseres ved hjælp af værktøjet sync_job_id.py (se ovenfor).

5. Eventuelt synkronisering fra AD

Hvis det ønskes at synkronisere adresser fra AD, skal scriptet ad_sync.py afvikles, settings til dette er beskrevet i afsnittet Integration til Active Directory

Ledere

SD Løn indeholder som udgangspunkt ikke information om, hvorvidt en ansat er leder. Det er derfor ikke muligt importere informaion om ledere direke fra dataudtrækket. Der er dog implementeret to metoder til at angive lederinformation:

  1. Inddirekte via JobPositionIdentifier

    Det er muligt at angive et antal værdier for JobPositionIdentifier som anses for at være ledere. Disse er i øjeblikket hårdkodet til værdierne 1030, 1040 og 1050. Hvis intet andet angives vil disse medarbejdere anses for at være ledere i de afdelinger de er ansat i.

  2. Via eksternt leveret fil.

    Integrationen understøtter at blive leveret en liste af ledere som kan importeres fra en anden kilde. Denne liste angives med parametren manager_rows ved opstart af importeren. Formatet for denne anivelse er

    manager_rows = [
    
        {'cpr': leders_cpr_nummer,
         'ansvar': 'Lederansvar'
         'afdeling': sd_enhedskode
        }
        ...
    ]
    

    Hvor lederansvar er en fritekststreng, alle unikke værdier vil blive oprettet under facetten responsibility i Klassifikation. Det er i den nuværende udgave ikke muligt at importere mere end et lederansvar pr leder.

AD Integration til SD import

SD Importen understøtter at anvende komponenten Integration til Active Directory til at berige objekterne fra SD Løn med information fra Active Directory. I de fleste tilfælde drejer dette sig som minimum om felterne ObjectGuid og SamAccountName men det er også muligt at hente eksempelvis telefonnumre eller stillingsbetegnelser.

Feltet ObjectGuid vil i MO blive anvendt til UUID for det tilhørende medarbejderobjekt, hvis ikke UUID’en allerede er givet fra en ekstern kilde. SamAccountName vil blive tilføjet som et brugernavn til IT systemet Active Direkctory for den pågældende bruger.

ChangedAt.db

For at holde rede på hvornår MO sidst er opdateret fra SD Løn, findes en SQLite database som indeholder to rækker for hver færdiggjort kørsel. Adressen på denne database er angivet i settings med nøglen integrations.SD_Lon.import.run_db.

Programmet db_overview.py er i stand til at læse denne database og giver et outut som dette:

id: 1, from: 2019-08-22 00:00:00, to: 2019-08-22 00:00:00, status: Running since 2019-08-22 14:03:01.226492
id: 2, from: 2019-08-22 00:00:00, to: 2019-08-22 00:00:00, status: Initial import: 2019-08-22 16:31:29.151569
id: 3, from: 2019-08-22 00:00:00, to: 2019-08-23 00:00:00, status: Running since 2019-08-23 09:00:04.215068
id: 4, from: 2019-08-22 00:00:00, to: 2019-08-23 00:00:00, status: Update finished: 2019-08-23 09:05:36.587527
id: 5, from: 2019-08-23 00:00:00, to: 2019-08-24 00:00:00, status: Running since 2019-08-28 08:44:11.181134
id: 6, from: 2019-08-23 00:00:00, to: 2019-08-24 00:00:00, status: Update finished: 2019-08-28 08:46:19.146615
id: 7, from: 2019-08-24 00:00:00, to: 2019-08-25 00:00:00, status: Running since 2019-08-28 08:49:27.479475
id: 8, from: 2019-08-24 00:00:00, to: 2019-08-25 00:00:00, status: Update finished: 2019-08-28 08:49:36.189767
id: 9, from: 2019-08-25 00:00:00, to: 2019-08-26 00:00:00, status: Running since 2019-08-28 08:50:42.929468
id: 10, from: 2019-08-25 00:00:00, to: 2019-08-26 00:00:00, status: Update finished: 2019-08-28 08:50:51.811845
id: 11, from: 2019-08-26 00:00:00, to: 2019-08-27 00:00:00, status: Running since 2019-08-28 08:54:46.207228
id: 12, from: 2019-08-26 00:00:00, to: 2019-08-27 00:00:00, status: Update finished: 2019-08-28 08:59:20.876762
id: 13, from: 2019-08-27 00:00:00, to: 2019-08-28 00:00:00, status: Running since 2019-08-28 09:07:25.961710
id: 14, from: 2019-08-27 00:00:00, to: 2019-08-28 00:00:00, status: Update finished: 2019-08-28 09:12:08.191701

Ved starten af alle changedAt kørsler, skrives en linje med status Running og efter hver kørsel skrives en linje med status Update finished. En changedAt kørsel kan ikke startes hvis den nyeste linje har status Running, da dette enten betyder at integrationen allerede kører, eller at den seste kørsel fejlede.

SD_MOX

SD-MOX er en udvidelse til SD-løn, som leveres af Silkeborg Data. SD-Mox muliggør opdatering af visse felter på organisationsenheder, som findes både i OS2MO og i SD-løn.

OS2MO’s integration til SD-mox involverer brug af SD-løns AMQP-kø til afsendelse af ændringer og oprettelser, hvorimod læsning og verifikation foregår via SD’s webinterface.

Integrationen er synkron, udført med triggere (se https://os2mo.readthedocs.io/en/latest/dev/triggers.html), således at forstå at man får svar umiddelbart i forbindelse med sin handling, som er en af

  1. oprettelse af organisatiorisk enhed
  2. omdøbning af organisatorisk enhed
  3. flytning af organisatorisk enhed
  4. ændring/oprettelse af adresser på en organisatorisk enhed

Konfiguration

Konfiguration af modulet er fleksibel og dermed lidt kompleks. For det første er der url, bruger og password mv. til SD’s webinterface som dokumenteret under SD løn opsætning . SD’s AMQP-opsætning er derimod specifik for SD-mox-modulet og udgøres af disse settings:

  • integrations.SD_Lon.sd_mox.AMQP_USER: AMQP bruger aftalt med SD
  • integrations.SD_Lon.sd_mox.AMQP_HOST: AMQP host aftalt med SD
  • integrations.SD_Lon.sd_mox.AMQP_PORT: AMQP port aftalt med SD
  • integrations.SD_Lon.sd_mox.AMQP_PASSWORD: AMQP password aftalt med SD
  • integrations.SD_Lon.sd_mox.AMQP_CHECK_RETRIES: Antal gange man prøver at validere de via AMQP overførte ændringer (default: 6)
  • integrations.SD_Lon.sd_mox.AMQP_CHECK_WAITTIME: Ventetid før hvert forsøg på validering (default: 3)
  • integrations.SD_Lon.sd_mox.VIRTUAL_HOST: Virtuel host aftalt med SD

Dernæst beskriver integrations.SD_Lon.sd_mox.TRIGGERED_UUIDS en liste af UUID-strenge for afdelinger på topniveau, som, inklusive undertræer, anses som forbundet med SD. Den kan se ud som ["e3e38b32-61c0-4900-a200-000001510002"], flere strenge adskilles af komma.

SD-løn anvender et begreb, som hedder NY-Niveauer, disse er reguleret sådan at man kan sætte en afdeling på Afdelings-niveau ind under en afdeling på NY1-niveau, men ikke omvendt. integrations.SD_Lon.sd_mox.OU_LEVELKEYS beskriver en liste af NY-niveauer i rækkefølge fra højere til lavere niveauer. Denne liste anvendes til at omsætte os2mos klasse-uuider for facetten org_unit_level til de tekst-strenge som SD-MOX forventer samt for at validere omtalte regler inden indsætning. Den ser typisk ud som ["NY6-niveau", "NY5-niveau",... ,"NY1-niveau", "Afdelings-niveau"]

Nogle kommuner anvender en facet, der hedder time_planning, og den setting, der hedder integrations.SD_Lon.sd_mox.OU_TIME_PLANNING_MO_VS_SD udgør en mapning imellem brugervendte nøgler for klasserne i time_planning og de strenge, der skal overføres til SD som repræsentation for samme. Den kan se ud som : {..., "DannesIkke": "Normaltjeneste dannes ikke"}

Anvendelse af SD-mox

Når man i OS2MOS grafiske klient arbejder med organisatoriske enheder i et undertræ, der er inkluderet i integrations.SD_Lon.sd_mox.TRIGGERED_UUIDS, vil flytninger, oprettelser, omdøbninger og tilføjelse/ændring af adresser bliver overført til SD. Der er dog visse begræsninger i input, som gennemgås nedenfor.

Der er en forsinkelse på 8.5 sekunder i brugerinterfacet mellem afsendelse imod SD og modtagelse af kvitteringen for ændringerne. Det er ikke SD, som har den forsinkelse; Den er indført i OS2MO fordi vi ikke får kvitteringen for ændringen direkte fra SD, men først ser den via at opslag på webinterfacet, og ei er nødt til at vente til vi forventer at SD er faldet til ro efter en ændring.

Begrænsninger i input

Der er en del begrænsninger i input, som er indført enten ud fra viden om SD’s krav eller slet og ret ved at prøve sig frem. Alle disse begrænsninger gælder kun i de OS2MO-undertræer, som er inkluderet i integrations.SD_Lon.sd_mox.TRIGGERED_UUIDS

  • Afdelingsnumre skal være med stort. Det er de hos SD.
  • Pnumre efter addresser. Det interface vi anvender hos SD kan kun vise Pnumre hvis der er en postadresse – derfor har vi indført et krav om postadresse, hvis man angiver Pnummer.
  • Afdelingsnumre skal være 2 til 4 karakterer lange i SD - denne begrænsning understøttes af SD-MOX
  • Ny Niveauer har ikke-tilladte forældre-barn-relationer, og der valideres inden vi forsøger at sætte noget ind hos SD.

SD-mox fejlmeddelelser

Der er en del mulige fejl, man kan begå, når man anvender OS2MO med denne integration tilkoblet. Der er gjort et stort arbejde for at fange dem, så man ikke kan lave en ændring i OS2MO, der ikke er reflekteret i SD. Der vises fejlmeddelser i OS2MO’s brugerinterface for at gøre opmærksom på dem og de er alle foranstillet prefixet Integrationsfejl, SD-Mox:

  • SD AMQP credentials mangler
  • Klasse-uuider for conf af Ny-Niveauer eller Tidsregistrering mangler
  • Uventet svar fra SD amqp
  • Startdato skal altid være den første i en måned
  • Afdeling ikke unik. Code {}, uuid {}, level {}
  • Enhedsnummer for kort
  • Enhedsnummer for langt
  • Ugyldigt tegn i enhedsnummer
  • Enhedsnummer skal være store bogstaver
  • Enhedsnummer er i brug
  • Forældrenheden findes ikke
  • Enhedstypen passer ikke til forældreenheden
  • Afdeling ikke fundet: %s
  • Følgende felter kunne ikke opdateres i SD
  • Enhedstype er ikke et kendt NY-niveau
  • Forældreenhedens enhedstype er ikke et kendt NY-niveau
  • Opret postaddresse før pnummer

Integration til OPUS Løn

Indledning

Denne integration gør det muligt at hente og opdatere organisations- og medarbejderoplysninger fra XML dumps fra OPUS Løn til OS2MO

Opsætning

For at kunne afvikle integrationen, kræves adgang til en mappe med xml-dumps fra OPUS. Oplysninger om stien til denne mappe er øjeblikket skrevet direkte i importkoden og kan ikke ændres i runtime.

Den forventede sti for mappen med opus dumps er: /opt/customer/

De enkelte dumps forventes at være navngivet systematisk som: ZLPE<data + tid>_delta.xml

Eksempelvis ZLPE20190902224224_delta.xml.

Nuværende implementeringslogik for import fra Opus:

  • Data indlæses i form at et xml-dump.
  • Hvis data indeholder information om enhedstyper, oprettes disse enhedstyper som klasser, hvis ikke, får alle enheder typen Enhed.
  • SE-, CVR-, EAN-, p-numre og telefon indlæses på enheder, hvis disse oplysninger er tilgængelige.
  • Hvis data indeholder postadresser på enheder eller medarejdere, slås disse adresser op på DAR, og hvis det er muligt at få en entydigt match, gemmes DAR-uuid’en på enheden eller personen. Adresser med adressebeskyttelse importeres ikke.
  • Telefon og email importeres for medarbejdere, hvis de findes i data.
  • Ansættelsestyper og titler oprettes som klasser og sættes på de tilhørende engagementer. Ansættelsestypen læses fra feltet workContractText, hvis dette eksisterer, hvis ikke får medarbejderen typen Ansat.
  • Information om ledere importeres direkte fra data, de to informationer superiorLevel og subordinateLevel konkateneres til et lederniveau.
  • Information om roller importeres direkte fra data.

IT-Systemer

En import fra OPUS vil oprette IT-systemet ‘Opus’ i MO. Alle medarbejdere som har en værdi i feltet userId vil få skrevet deres OPUS brugernavn på dette IT-system.

AD-Integration

OPUS Importen understøtter at anvende komponenten Integration til Active Directory til at berige objekterne fra OPUS med information fra Active Directory. I øjebliket er det muligt at importere felterne ObjectGuid og SamAccountName.

Hvis AD integrationen er aktiv, vil importeren oprette IT-systemet ‘Active Directory’ og oprette alle brugere der findes i AD med brugernavnet fundet i SamAccountName. Brugere med en AD konto vil blive oprettet med deres AD ObjectGuid som UUID på deres brugerobjekt, med mindre de er angivet i en cpr-mapning.

cpr-mapning

For at kunne lave en frisk import uden at få nye UUID’er på medarbejderne, er det muligt at give importen adgang til et csv-udtræk som parrer cpr-numre med UUID’er. Disse UUID’er vil altid få forrang og garanterer derfor at en medarbejder får netop denne UUID, hvis vedkommendes cpr-nummer er i csv-udtrækket. Udtrækket kan produceres fra en kørende instans af MO ved hjælp ved værktøkjet cpr_uuid.py, som findes under exports.

Primær ansættelse

I XML dumps fra Opus findes ikke et koncept om primæransættelse, men da AD integrationen til MO har behov for at kunne genkende den primære ansættelse til synkronisering, bestemmes dette ud fra en beregning:

Den mest afgørende komponent af beregningen foregår på baggrund af ansættelestypen, hvor en liste af uuid’er i settings.json bestemmer hvilke ansættelstyper der anses for at være mest primære. Hvis flere engagementer har den samme ansættelsestype, vælges ansættelsen med det laveste ansættelsenummer. Hvis en ansættelse er manuelt angivet til at være primær, vil denne ansættelse altid regnes som primær.

Informationen om primæransætelse opretholdes i MOs facet primary_type, som ved import fra Opus XML altid populeres med disse tre klasser:

  • Manuelt primær ansættelse: Dette felt angiver at en ansættelse manuelt er sat til at være primær
  • Ansat: Angiver en medarbejders beregnede primære ansættelse.
  • Ikke-primær ansat: Angiver alle andre ansættelser for en medarbejder.

Manuelt primær optræder ikke direkte i imports, men kan sættes manuelt fra MOs GUI. De øvrige primærklasser håndteres af Opus integrationen, og må ikke sættes manuelt.

En medarbejder skifter ikke ansættelsestype selvom vedkommende fratræder sit engagement. En ansættelses aktuelle status angives i stedet via MOs start- og slutdato. Er slutdato’en i fortiden, er vedkommende ikke længere ansat og vil i MOs gui fremgå i fanen fortid. Er en medarbejers startdato i fremtiden, er personen endnu ikke tiltrådt, og fremgår i fanen fremtid.

Anvendelse af integrationen

For at anvende integrationen kræves udover de nævnte xml-dumps, at der oprettes en gyldig konfiguration i settings.json. De påkrævede nøgler er:

  • mox.base: Adressen på LoRa.
  • mora.base: Adressen på MO.
  • opus.import.run_db: Stien til den database som gemmer information om kørsler af integrationen. Hvis integrationen skal køre som mere end et engangsimport har denne fil en vigtig betydning.
  • municipality.name: Navnet på kommunen.
  • crontab.SAML_TOKEN: saml token til forbindelse til OS2MO

Til at hjælpe med afviklingen af importen, findes en hjælpefunktion i opus_helpers.py som afvikler selve importen og initialiserer databasen i opus.import.run_db korrekt. Dette modul forventer at finde en cpr-mapning og vil fejle hvis ikke filen settings/cpr_uuid_map.csv eksisterer.

Førstegangsimport (initialindlæsning)

Hvis den nuværende import er den første, findes der i reglen ikke nogen mapning, og der må så oprettes en tom fil i dens sted (settings/cpr_uuid_map.csv)

før kaldet af initialindlæsning skal SAML_TOKEN være defineret i environment. Det kan man få igennem at source (dotte) tools/prefixed_settings.sh når man, som det sig hør og bør, er placeret i roden af directoriet os2mo-data-import-and-export.

Ligeledes må databasen, som er defineret i opus.import.run_db ikke findes og lora-databasen skal være tom.

Løbende opdatering af Opus data i MO

Der er skrevet et program som foretager løbende opdateringer til MO efterhåden som der sker ændringer i Opus data. Dette foregår ved, at integrationen hver gang den afvikles, kigger efter det ældste xml-dump som endnu ikke er importeret og importerer alle ændringer i dette som er nyere end den seneste importering. Et objekt regnes som opdateret hvis parameteren lastChanged på objektet er nyere end tidspunktet for det senest importerede xml-dump. Alle andre objekter ignoreres.

Hvis et objekt er nyt, foretages en sammenligning af de enkelte felter, og de som er ændret, opdateres i MO med virkning fra lastChanged datoen. En undtagelse for dette er engagementer, som vil blive oprettet med virkning fra entryDate datoen, og altså således kan oprettes med virkning i fortiden.

Også opdateringsmodulet forventer at finde en cpr-mapning, som vil blive anvendt til at knytte bestemte UUID’er på bestemte personer, hvis disse har været importeret tidligere. Denne funktionalitet er nyttig, hvis man får brug for at re-importere alle Opus-data, og vælger at arbejde sig igennem gamle dumps for at importere historik. I daglig brug vil mapningen ikke have nogen betydning, da oprettede brugere her altid vil være nye.

Opdatering af enkelte brugere

Skulle det af den ene eller den anden grund ske, at en bruger ikke er importeret korrekt, er det muligt at efterimportere denne bruger. Funktionen er endnu ret ny og det tilrådes derfor altid at tage en backup af databasen før den benyttes. Funktionen fungerer ved at hente historiske data fra gamle xml-dumps, og det er derfor en forudsætning, at disse dumps stadig er til rådighed. For at synkronisere en enkelt medarbejder anvedes disse kommandolinjeparametre:

  • --update-single-user: Ansættelsesnummer på den relevante medarbejder
  • days: Antal dage bagud integrationen skal søge.

Nuværende begrænsninger omkring re-import

  • IT-systemer tilknyttes kun i forbindelse med oprettelsen af en medarbejder, de tildeles uendelig virkning og nedlægges aldrig.
  • Ændringer i roller håndteres kun ved ændringer i slutdatoer, det antages at startdatoer ikke ændres.
  • Tomme ændringer på en leder opdages ikke, så der opstår en ekstra række på lederobjekter hvis en leder ændres. Den resulterende tilstand er korrekt, men indeholder en kunstig skæringsdato i sin historik.
  • Der oprettes ikke automatisk nye engagementstyper, alle engagementer forventes at have en type som blev oprettet ved førstegangsimporten.
  • Der oprettes ikke automatisk nye lederniveauer, alle ledere forventes at have et niveau som eksisterede ved førstegangsimporten.

run_db.sqlite

For at holde rede på hvornår MO sidst er opdateret fra Opus, findes en SQLite database som indeholder to rækker for hver færdiggjort kørsel. Adressen på denne database er angivet i settings.json under nøglen opus.import.run_db.

Programmet db_overview.py er i stand til at læse denne database og giver et outut som dette:

id: 1, dump date: 2019-09-02 22:41:28, status: Running since 2019-11-19 08:32:30.575527
id: 2, dump date: 2019-09-02 22:41:28, status: Import ended: 2019-11-19 08:55:32.455146
id: 3, dump date: 2019-09-03 22:40:12, status: Running diff update since 2019-11-19 10:18:35.859294
id: 4, dump date: 2019-09-03 22:40:12, status: Diff update ended: 2019-11-19 10:19:15.806079
id: 5, dump date: 2019-09-04 22:40:12, status: Running diff update since 2019-11-19 10:19:16.006959
id: 6, dump date: 2019-09-04 22:40:12, status: Diff update ended: 2019-11-19 10:19:48.980694
id: 7, dump date: 2019-09-05 22:40:12, status: Running diff update since 2019-11-19 10:19:49.187977
id: 8, dump date: 2019-09-05 22:40:12, status: Diff update ended: 2019-11-19 10:20:23.547771
id: 9, dump date: 2019-09-06 22:40:13, status: Running diff update since 2019-11-19 10:20:23.745032
id: 10, dump date: 2019-09-06 22:40:13, status: Diff update ended: 2019-11-19 10:20:54.931163
id: 11, dump date: 2019-09-09 22:40:12, status: Running diff update since 2019-11-19 10:20:55.123478
id: 12, dump date: 2019-09-09 22:40:12, status: Diff update ended: 2019-11-19 10:21:35.481189
id: 13, dump date: 2019-09-10 22:40:12, status: Running diff update since 2019-11-19 10:21:35.682252
id: 14, dump date: 2019-09-10 22:40:12, status: Diff update ended: 2019-11-19 10:22:12.298526
id: 15, dump date: 2019-09-11 22:41:48, status: Running diff update since 2019-11-19 10:22:12.496829
id: 16, dump date: 2019-09-11 22:41:48, status: Diff update ended: 2019-11-19 10:22:45.317372
id: 17, dump date: 2019-09-12 22:40:12, status: Running diff update since 2019-11-19 10:22:45.517679
id: 18, dump date: 2019-09-12 22:40:12, status: Diff update ended: 2019-11-19 10:23:20.548220
id: 19, dump date: 2019-09-13 22:40:14, status: Running diff update since 2019-11-19 10:23:20.744435
id: 20, dump date: 2019-09-13 22:40:14, status: Diff update ended: 2019-11-19 10:23:51.416625
id: 21, dump date: 2019-09-16 22:40:12, status: Running diff update since 2019-11-19 10:23:51.610555
id: 22, dump date: 2019-09-16 22:40:12, status: Diff update ended: 2019-11-19 10:24:44.799932
id: 23, dump date: 2019-09-17 22:40:12, status: Running diff update since 2019-11-19 10:24:45.000445
id: 24, dump date: 2019-09-17 22:40:12, status: Diff update ended: 2019-11-19 10:25:25.651491
(True, 'Status ok')

Ved starten af alle opus_diff_import kørsler, skrives en linje med status Running og efter hver kørsel skrives en linje med status Diff update ended. En kørsel kan ikke startes hvis den nyeste linje har status Running, da dette enten betyder at integrationen allerede kører, eller at den seneste kørsel fejlede.

Filtrering af organisationsenheder

Den valgfrie nøgle integrations.opus.units.filter_ids kan sættes for at filtrere udvalgte organisationenheder og deres tilhørende underliggende organisationsenheder fra, før selve importen kører.

Nølgen skal være en liste indeholdende OPUS ID’er for de organisationsenheder, som ønskes filtreret fra. OPUS ID findes i OPUS filen <orgUnit id="350" client="813" lastChanged="2020-09-16">

Integration til Active Directory

Indledning

Denne integration gør det muligt at læse information fra en lokal AD installation med henblik på at anvende disse informationer ved import til MO.

Opsætning

For at kunne afvikle integrationen kræves en række opsætninger af den lokale server.

Integrationen går via i alt tre maskiner:

  1. Den lokale server, som afvikler integrationen (typisk MO serveren selv).
  2. En remote management server som den lokale server kan kommunikere med via Windows Remote Management (WinRM). Denne kommunikation autentificeres via Kerberos. Der findes en vejledning til opsætning med kerberos her: https://os2mo.readthedocs.io/en/latest/_static/AD%20-%20OS2MO%20ops%C3%A6tnings%20guide.pdf Alternativt kan der autentificeres med ntlm over https. Denne opsætning beskrives herunder.
  3. AD serveren.

Når integrationen er i drift, genererer den PowerShell kommandoer som sendes til remote management serveren som afvikler dem mod AD serveren. Denne omvej hænger sammen med, at MO afvikles fra et Linux miljø, hvorimod PowerShell kommunikation med AD bedst afvikles fra et Windows miljø.

For at kunne afvikle integrationen kræves der udover den nævnte opsætning af enten Kerberos eller ntlm, at AD er sat op med cpr-numre på medarbejdere samt en servicebruger som har rettigheder til at læse dette felt. Desuden skal et antal variable være sat i settings.json

Det er muligt at anvende flere AD til udlæsning af adresser og itsystemer til OS2MO Således er integrations.ad i settings.json et array med følgende indbyggede betydning:

  • Første AD i listen (index 0) anvendes til skrivning (hvis skrivning er aktiveret) og til integrationer, som endnu ikke er forberedt for flere ad’er.
  • Alle AD’er anvendes af ad_sync til opdatering af og skabelse af adresser, itsystemer

Opsætning af ntlm over https

For at kunne autentificere med ntlm over https kræver det at settingsfilen indeholder brugernavn og password til en systembruger fra et domæne - modsat lokalt oprettet bruger - samt metoden ‘ntlm’. Se bekrivelsen af parametre herunder. Brugeren skal desuden have administratorrettigheder på windowsserveren, samt rettigheder til at læse og evt. skrive i AD. Dette gælder også feltet der indeholder CPR numre der kan være indstillet til ‘confidential’. I så fald skal rettigheden gives gennem programmet ldp. For at sætte winrm op med https vha. et SelfSignedCertificate kan man følge nedenstående: Erstat “Computernavn” med serverens Hostname.

  1. I powershell som administrator køres
New-SelfSignedCertificate -DnsName "Computernavn" -CertStoreLocation Cert:\LocalMachine\My

Det giver et ‘thumbprint’ i stil med “54B8571D6D0C0C89473ED5470A45EDC5A68AA2C3”

  1. Dette sættes ind i følgende kommando i en kommandoprompt (ikke powershell) også som administrator:

derefter i administrator powershell igen:

netsh advfirewall firewall add rule name="WinRM-HTTPS" dir=in localport=5986 protocol=TCP action=allow
winrm quickconfig -q
winrm quickconfig -transport:http
$enableArgs=@{Force=$true}
try {
  $command=Get-Command Enable-PSRemoting
  if($command.Parameters.Keys -contains "skipnetworkprofilecheck"){
      $enableArgs.skipnetworkprofilecheck=$true
  }
}
catch {
  $global:error.RemoveAt(0)
}
Enable-PSRemoting @enableArgs
winrm set winrm/config/client/auth '@{Basic="true"}'
winrm set winrm/config/service/auth '@{Basic="true"}'
winrm set winrm/config/service '@{AllowUnencrypted="false"}'

Nu skulle der være adgang til winrm med ntlm, krypteret med https, via port 5986.

Fælles parametre

  • integrations.ad.winrm_host: Hostname på remote mangagent server

For hvert ad angives

  • search_base: Search base, eksempelvis ‘OU=enheder,DC=kommune,DC=local’
  • cpr_field: Navnet på feltet i AD som indeholder cpr nummer.
  • cpr_separator: Angiver en eventuel separator mellem fødselsdato og løbenumre i cpr-feltet i AD. Hvis der ikke er en separator, angives en tom streng.
  • sam_filter: Hvis denne værdi er sat, vil kun det være muligt at cpr-fremsøge medarbejder som har denne værdi foranstillet i SAM-navn. Funktionen muliggør at skelne mellem brugere og servicebrugere som har samme cpr-nummer.
  • caseless_samname: Hvis denne værdi er true (Default) vil sam_filter ikke se forskel på store og små bogstaver.
  • system_user: Navnet på den systembruger som har rettighed til at læse fra AD.
  • password: Password til samme systembruger.
  • properties: Liste over felter som skal læses fra AD. Angives som en liste i json-filen.
  • method: Metode til autentificering - enten ntlm eller kerberos. Hvis denne ikke er angivet anvendes kerberos.
  • servers - domain controllere for denne ad.

Test af opsætningen

Der følger med AD integrationen et lille program, test_connectivity.py som tester om der kan læses fra eller skrives til AD, og dermed at autentificering er konfigureret korrekt. Programmet afvikles med en af to parametre:

  • --test-read-settings
  • --test-write-settings
En test af læsning foregår i flere trin:
  • Der testes for om Remote Management serveren kan nås og autentificeres med metoden specificeret i settings - enten Kerberos (standard) eller med ntlm.
  • Der testes om det er muligt af afvikle en triviel kommando på AD serveren.
  • Der testes for, at en søgning på alle cpr-numre fra 31. november returnerer nul resultater.
  • Der testes for, at en søging på cpr-numre fra den 30. i alle måneder returnerer mindst et resultat. Hvis der ikke returneres nogen, er fejlen sandsynligvis en manglende rettighed til at læse feltet med cpr-nummer i AD. Dette kan bla. skyldes at rettigheder til confidential attributes skal sættes i ldp programmet.
  • Der testes om de returnerede svar indeholder mindst et eksempel på disse tegn: æ, ø, å, @ som en test af at tegnsættet er korrekt sat op.

En test af skrivning foregår efter denne opskrift:

  • Der testes for om de nødvendige værdier er til stede i settings.json, det drejer sig om nøglerne: * integrations.ad.write.uuid_field: AD feltet som rummer MOs bruger-UUID * integrations.ad.write.level2orgunit_field: AD feltet hvor MO skriver den primære organisatoriske gruppering (direktørområde, forvaltning, etc.) for brugerens primære engagement. * integrations.ad.write.org_unit_field: Navnet på det felt i AD, hvor MO skriver enhedshierakiet for den enhed, hvor medarbejderen har sin primære ansættelse. * integrations.ad.write.upn_end: Endelse for feltet UPN. * integrations.ad.write.level2orgunit_type: UUID på den klasse som beskriver at en enhed er den primære organisatoriske gruppering (direktørområde, forvaltning, etc.). Dette kan være en enhedstype eller et enhedsniveau.
  • Der udrages et antal tilfældige brugere fra AD (mindst 10), og disse tjekkes for tilstædeværelsen af de tre AD felter beskrevet i integrations.ad.write.uuid_field, integrations.ad.write.level2orgunit_field og integrations.ad.write.org_unit_field. Hvis hvert felt findes hos mindst en bruger, godkendes den lokale AD opsætning.
  • Længden af cpr-numrene hos de tilfældige brugere testes for om de har den forventede længde, 10 cifre hvis der ikke anvendes en separator, 11 hvis der gør. Det er et krav for at integrationen kan køre korrekt, at alle cpr-numre anvender samme (eller ingen) separator.

Hvis disse tests går igennem, anses opsætningen for at være klar til AD skriv integrationen.

Brug af integrationen

Integrationen anvendes ved at slå brugere op via cpr nummer. Det er muligt at slå op på enten et specifikt cpr-nummer, på en søgning med wild card, eller man kan lave et opslag på alle brugere, som derved caches i integrationen hvorefter opsalg på enkelte cpr-numre vil ske næsten instantant. Den indledende cache skabes i praksis ved at itererere over alle cpr-numre ved hjælp af kald til 01*, 02* etc.

Ved anvendelse af både administrativt AD og skole AD vil brugere først blive slået op i skole AD og dernæst i administrativt AD, hvis medarbejderen findes begge steder vil det således blive elementet fra det administrative AD som vil ende med at blive returneret.

import ad_reader

ad_reader = ad_reader.ADParameterReader()

# Læs alle medarbejdere ind fra AD.
ad_reader.cache_all()

# De enkelte opslag går nu direkte til cache og returnerer med det samme
user = ad_reader.read_user(cpr=cpr, cache_only=True)

Objektet user vil nu indeholde de felter der er angivet i settings.json med nøglen integrations.ad.properties.

Valg af primær konto ved flere konti pr. cprnummer

Nogle steder har man flere konti med samme cprnummer i AD’et. For at vælge den primære, som opdaterer / opdateres fra MO, kan man anvende et sæt nøgler i settingsfilen:

  • integrations.ad.discriminator.field et felt i det pågældende AD, som bruges til at afgøre hvorvidt denne konto er den primære
  • integrations.ad.discriminator.values et sæt strenge, som matches imod integrations.ad.discriminator field
  • integrations.ad.discriminator.function kan være ‘include’ eller ‘exclude’

Man definerer et felt, som indeholder en indikator for om kontoen er den primære, det kunnne f.x være et felt, man kaldte xBrugertype, som kunne indeholde “Medarbejder”.

Hvis man i dette tilfælde sætter integrations.ad.discriminator.function til include vil kontoen opfattes som primær hvis ‘Medarbejder’ også findes i integrations.ad.discriminator.values

Opfattes mere end en konto som primær tages den første, man støder på - I så tilfælde fungerer integrations.ad.discriminator.values som en prioriteret liste

Findes nøglen integrations.ad.discriminator.field, skal de andre to nøgler også være der. Findes den ikke, opfattes alle AD-konti som primære.

Skrivning til AD

Der udvikles i øjeblikket en udvidelse til AD integrationen som skal muliggøre at oprette AD brugere og skrive information fra MO til relevante felter.

Hvis denne funktionalitet skal benyttes, er der brug for yderligere parametre som skal være sat når programmet afvikles:

  • servers fra integrations.ad[0]: Liste med de DC’ere som findes i kommunens AD. Denne liste anvendes til at sikre at replikering er færdiggjort før der skrives til en nyoprettet bruger.
  • integrations.ad.write.uuid_field: Navnet på det felt i AD, hvor MOs bruger-uuid skrives.
  • integrations.ad.write.level2orgunit_field: Navnet på det felt i AD, hvor MO skriver navnet på den organisatoriske hovedgruppering (Magistrat, direktørområde, eller forvalting) hvor medarbejderen har sin primære ansættelse.
  • integrations.ad.write.org_unit_field: Navnet på det felt i AD, hvor MO skriver enhedshierakiet for den enhed, hvor medarbejderen har sin primære ansættelse.
  • integrations.ad.write.primary_types: Sorteret lister over uuid’er på de ansættelsestyper som markerer en primær ansættelse. Jo tidligere et engagement står i listen, jo mere primært anses det for at være.
  • integrations.ad.write.level2orgunit_type: uuid på den enhedstype som angiver at enheden er en organisatorisk hovedgruppering og derfor skal skrives i feltet angivet i integrations.ad.write.level2orgunit_field.
  • integrations.ad.write.create_user_trees: Liste over uuid’er på enheder, medarbejdere i disse enheder samt deres underheder, vil få oprettet AD en konto af scriptet ad_life_cycle.py hvis de ikke har en i forvejen.

Skabelse af brugernavne

For at kunne oprette brugere i AD, er det nødvendigt at kunne tildele et SamAccountName til de nye brugere. Til dette formål findes i modulet user_names klassen CreateUserNames. Programmet startes ved at instantiere klassen med en liste over allerede reserverede eller forbudte navne som parametre, og det er herefter muligt at forespørge AD om en liste over alle brugenavne som er i brug, og herefter er programet klar til at lave brugernavne.

from user_names import CreateUserName

name_creator = CreateUserNames(occupied_names=set())
name_creator.populate_occupied_names()

name = ['Karina', 'Munk', 'Jensen']
print(name_creator.create_username(name))

name = ['Anders', 'Kristian', 'Jens', 'Peter', 'Andersen']
print(name_creator.create_username(name))

name = ['Olê', 'Østergård', 'Høst', 'Ærøe']
print(name_creator.create_username(name))

Brugernavne konstrureres efter en forholdsvis specifik algoritme som fremgår af koden.

Synkronisering

Der eksisterer (udvikles) to synkroniseringstjenester, en til at synkronisere felter fra AD til MO, og en til at synkronisere felter fra MO til AD.

AD til MO

Synkronisering fra AD til MO foregår via programmet ad_sync.py.

Programmet opdaterer alle værdier i MO i henhold til den feltmapning som er angivet i settings.json. Det er muligt at synkronisere adresseoplysninger, samt at oprette et IT-system på brugeren, hvis brugeren findes i AD, men endnu ikke har et tilknyttet IT-system i MO. Desuden er det muligt at synkronisere et AD felt til et felt på brugerens primærengagement (typisk stillingsbetegnelsen). Husk at efterfølgende AD kan overskrive. Derfor: Anvend ikke samme klasser, itsystemer eller extensionfelter i flere af de specificerede AD’er

Et eksempel på en feltmapning angives herunder:

"ad_mo_sync_mapping": {
    "user_addresses": {
        "telephoneNumber": ["a6dbb837-5fca-4f05-b369-8476a35e0a95", "INTERNAL"],
        "pager": ["d9cd7a04-a992-4b31-9534-f375eba2f1f4 ", "PUBLIC"],
        "EmailAddress": ["fbd70da1-ad2e-4373-bb4f-2a431b308bf1", null],
        "mobile": ["6e7131a0-de91-4346-8607-9da1b576fc2a ", "PUBLIC"]
    },
    "it_systems": {
        "samAccountName": "d2998fa8-9d0f-4a2c-b80e-c754c72ef094"
    },
    "engagements": {
        "Title": "extension_2"
    }
}

For adresser angives en synlighed, som kan antage værdien PUBLIC, INTERNAL, SECRET eller null som angiver at synligheden i MO sættes til henholdsvis offentlig, intern, hemmelig, eller ikke angivet. UUID’er er på de tilhørende adresseklasser i MO som AD felterne skal mappes til.

Hvis der findes flere adresser i MO med samme type og synlighed, springer programmet den givne adresse over, og skriver en advarsel i loggen. Det forventes herefter at brugeren af programmet løser denne situation, enten ved at sikre unikheden direkte, eller ved oprettelse af en speciel adresseklasse som udelukkende benyttes af AD, hvormed unikheden sikres ad den vej.

Hvis der for en given bruger er felter i feltmapningen som ikke findes i AD, vil disse felter bliver sprunget over, men de øvrige felter vil stadig blive synkroniseret.

Selve synkroniseringen foregår ved at programmet først udtrækker samtlige medarbejdere fra MO, der itereres hen over denne liste, og information fra AD’et slås op med cpr-nummer som nøgle. Hvis brugeren findes i AD, udlæses alle parametre angivet i integrations.ad.properties og de af dem som figurerer i feltmapningen synkroniseres til MO.

Integrationen vil som udgangspunkt ikke synkronisere fra et eventuelt skole AD, med mindre nøglen integrations.ad.skip_school_ad_to_mo sættes til false.

Da AD ikke understøtter gyldighedstider, antages alle informationer uddraget fra AD at gælde fra ‘i dag’ og til evig tid. Den eneste undtagelse til dette er ved afslutning af deaktiverede AD brugere.

Deaktiverede AD brugere kan håndteres på forskellige måder. Som udgangspunkt synkroniseres de på præcis samme vis som almindelige brugere, med mindre nøglen integrations.ad.ad_mo_sync_terminate_disabled er sat til True. Hvis dette er tilfælde ophører den automatiske synkronisering, og deaktiverede brugere får deres AD data ‘afsluttet’. Ved afslutning forstås at brugerens AD synkroniserede adresser og it-systemer flyttes til fortiden, såfremt de har en åben slutdato.

Slutteligt skal det nævnes, at implemeneringen af synkroniseringen understøtter muligheden for at opnå en betydelig hastighedsforbering ved at tillade direkte adgang til LoRa, denne funktion aktiveres med nøglen integrations.ad.ad_mo_sync_direct_lora_speedup og reducerer kørselstiden betragteligt. Hvis der er få ændringer vil afviklingstiden komme ned på nogle få minutter.

MO til AD

Synkronisering fra MO til AD foregår efter en algoritme hvor der itereres hen over alle AD brugere. Hver enkelt bruger slås op i MO via feltet angivet i nøglen integrations.ad.write.uuid_field og informatione fra MO synkroniseres til AD i henhold til den lokale feltmapning. AD-integrationen stiller et antal værdier til rådighed, som det er muligt at synkronisere til felter i AD. Flere kan tilføjes efterhånden som integrationen udvikles.

  • employment_number: Lønsystemets ansættelsesnummer for medarbejderens primære engagement.
  • end_date: Slutdato for længste ansættelse i MO, hvis en ansættelse ikke har nogen kendt slutdato, angives 9999-12-31.
  • uuid: Brugerens UUID i MO.
  • title: Stillingsbetegnelse for brugerens primære engagement.
  • unit: Navn på enheden for brugerens primære engagement.
  • unit_uuid: UUID på enheden for brugerens primære engagement.
  • unit_user_key: Brugervendt nøgle for enheden for brugerens primære engagement, dette vil typisk være lønssystemets kortnavn for enheden.
  • unit_public_email: Email på brugerens primære enhed med synligheen offentlig
  • unit_secure_email: Email på brugerens primære enhed med synligheen hemmelig. Hvis enheden kun har email-adresser uden angivet synlighed, vil den blive agivet her.
  • unit_postal_code: Postnummer for brugerens primære enhed.
  • unit_city: By for brugerens primære enhed.
  • unit_streetname: Gadenavn for brugerens primære enhed.
  • location: Fuld organisatorisk sti til brugerens primære enhed.
  • level2orgunit: Den oganisatoreiske hovedgruppering (Magistrat, direktørområde, eller forvalting) som brugerens primære engagement hører under.
  • manager_name: Navn på leder for brugerens primære engagement.
  • manager_cpr: CPR på leder for brugerens primære engagement.
  • manager_sam: SamAccountName for leder for brugerens primære engagement.
  • manager_mail: Email på lederen for brugerens primære engagement.

Felterne level2orgunit og location synkroniseres altid til felterne angivet i nøglerner integrations.ad.write.level2orgunit_type og integrations.ad.write.org_unit_field, og skal derfor ikke specificeres yderligere i feltmapningen.

Desuden synkroniseres altid AD felterne:
  • Displayname: Synkroniseres til medarbejderens fulde navn
  • GivenName: Synkroniseres til medarbejderens fornavn
  • SurName: Synkroniseres til medarbejderens efternavn
  • Name: Synkroniseres til vædien “Givenname Surname - Sam_account_name
  • EmployeeNumber: Synkroniseres til employment_number

Yderligere synkronisering fortages i henhold til en lokal feltmaping, som eksempelvis kan se ud som dette:

"integrations.ad_writer.mo_to_ad_fields": {
     "unit_postal_code": "postalCode",
     "unit_city": "l",
     "unit_user_key": "department",
     "unit_streetname": "streetAddress",
     "unit_public_email": "extensionAttribute3",
     "title": "Title",
     "unit": "extensionAttribute2"
}

Formattet for denne skal læses som: MO felt –> AD felt, altså mappes unit_public_email fra MO til extensionAttribute3 i AD.

Som et alternativ til denne direkte 1-til-1 felt-mapning er der mulighed for en mere fleksibel mapning vha. jinja skabeloner (Se eventuelt her: https://jinja.palletsprojects.com/en/2.11.x/templates/ (Engelsk)).

Brug af jinja skabelon for AD feltmapning, kan eksempelvis se ud som dette:

"integrations.ad_writer.template_to_ad_fields": {
     "postalCode": "{{ mo_values['unit_postal_code'] }}",
     "department": "{{ mo_values['unit_user_key'] }}",
     "streetName": "{{ mo_values['unit_streetname'].split(' ')[0] }}",
 "extensionAttribute3": "{{ mo_values['unit_public_email']|default('all@afdeling.dk') }}",
}

Det er værd at bemærke at begge systemer; mo_to_ad_fields og template_to_ad_fields benytter jinja systemet i maven på eksporteren.

Det er altså ækvivalent at skrive henholdvis:

"integrations.ad_writer.mo_to_ad_fields": {
     "unit_postal_code": "postalCode",
}

og:

"integrations.ad_writer.template_to_ad_fields": {
     "postalCode": "{{ mo_values['unit_postal_code'] }}",
}

Da førstnævnte konverteres til sidstnævnte internt i programmet.

Afvikling af PowerShell templates

Det er muligt at angive PowerShell kode hvor visse værdier angives med abstrakte refrencer til MO, som så på runtime vil bive udfyldt med de tilhørende værdier for det person det drejer sig om.

for øjeblikket understøttes disse variable:

  • %OS2MO_AD_BRUGERNAVN%
  • %OS2MO_BRUGER_FORNAVN%
  • %OS2MO_BRUGER_EFTERNAVN%
  • %OS2MO_BRUGER_CPR%
  • %OS2MO_LEDER_EMAIL%
  • %OS2MO_LEDER_NAVN%
  • %OS2MO_BRUGER_ENHED%
  • %OS2MO_BRUGER_ENHED_UUID%

Hvis et script indeholder andre nøgler på formen %OS2MO_ … % vil der returneres en fejlmeddelelse (exception hvis det afvikles som en integration), med mindre disse variable er udkommenteret.

Integrationen forventer at scripts befinder sig i mappen scripts i en undermappe til integrationen selv, og alle scripts skal have en ps_template som filendelse. Den tekniske platform for afvikling af scripts er den samme som for den øvrige AD integration; scriptet sendes til remote management serveren, som afvikler scriptet. Bemærk at scripts i denne kategori ikke nødvendigvis behøver have direkte kontakt med AD, men vil kunne anvends til alle formål hvor der er behov for at afvikle PowerShell med værdier fra MO.

Opsætning for lokal brug af integrationen

Flere af værktøjerne i AD integrationen er udstyret med et kommandolinjeinterface, som kan anvendes til lokale tests. For at anvende dette er skal tre ting være på plads i det lokale miljø:

  1. En lokal bruger med passende opsætning af kerberos til at kunne tilgå remote management serveren.
  2. Den nødvendige konfiguration skal angives i settings.json.
  3. Et lokalt pythonmiljø med passende afhængigheder

Angående punkt 1 skal dette opsættes af den lokale IT organisation, hvis man har fulgt denne dokumentation så langt som til dette punkt, er der en god sandsynlighed for at man befinder sig i et miljø, hvor dette allerede er på plads.

Punkt 2 gøres ved at oprette filen settings.json under mappen settings Et eksempel på sådan en fil kunne se sådan ud:

{
    "mox.base": "http://localhost:8080",
    "mora.base": "http://localhost:5000",
    "municipality.name": "Kommune Kommune",
    "municipality.code": 999,
    "integrations.SD_Lon.import.too_deep": ["Afdelings-niveau"],
    "integrations.SD_Lon.global_from_date": "2019-10-31",
    "integrations.SD_Lon.sd_user": "SDUSER",
    "integrations.SD_Lon.sd_password": "SDPASSWORD",
    "integrations.SD_Lon.institution_identifier": "AA",
    "integrations.SD_Lon.import.run_db": "/home/mo/os2mo-data-import-and-export/settings/change_at_runs.db",
    "address.visibility.secret": "53e9bbec-dd7b-42bd-b7ee-acfbaf8ac28a",
    "address.visibility.internal": "3fe99cdd-4ab3-4bd1-97ad-2cfb757f3cac",
    "address.visibility.public": "c5ddc7d6-1cd2-46b0-96de-5bfd88db8d9b",
    "integrations.ad.winrm_host": "rm_mangement_hostname",
    "integrations.ad.search_base": "OU=KK,DC=kommune,DC=dk",
    "integrations.ad.system_user": "serviceuser",
    "integrations.ad.password": "sericeuser_password",
    "integrations.ad.cpr_field": "ad_cpr_field",
    "integrations.ad.write.servers": [
        "DC1",
        "DC2",
        "DC3",
        "DC4",
        "DC5"
    ],
    "integrations.ad.write.level2orgunit_type": "cdd1305d-ee6a-45ec-9652-44b2b720395f",
    "integrations.ad.write.primary_types": [
        "62e175e9-9173-4885-994b-9815a712bf42",
        "829ad880-c0b7-4f9e-8ef7-c682fb356077",
        "35c5804e-a9f8-496e-aa1d-4433cc38eb02"
    ],
    "integrations.ad_writer.mo_to_ad_fields": {
        "unit_user_key": "department",
        "level2orgunit": "company",
        "title": "Title",
        "unit": "extensionAttribute2"
    },
    "integrations.ad.write.uuid_field": "sts_field",
    "integrations.ad.write.level2orgunit_field": "extensionAttribute1",
    "integrations.ad.write.org_unit_field": "extensionAttribute2",
    "integrations.ad.properties": [
        "manager",
        "ObjectGuid",
        "SamAccountName",
        "mail",
        "mobile",
        "pager",
        "givenName",
        "l",
        "sn",
        "st",
        "cn",
        "company",
        "title",
        "postalCode",
        "physicalDeliveryOfficeName",
        "extensionAttribute1",
        "extensionAttribute2",
        "ad_cpr_field"
    ],
    "integrations.ad.ad_mo_sync_mapping": {
        "user_addresses": {
            "telephoneNumber": ["51d4dbaa-cb59-4db0-b9b8-031001ae107d", "PUBLIC"],
            "pager": ["956712cd-5cde-4acc-ad0a-7d97c08a95ee", "SECRET"],
            "mail": ["c8a49f1b-fb39-4ce3-bdd0-b3b907262db3", null],
            "physicalDeliveryOfficeName": ["7ca6dfb1-5cc7-428c-b15f-a27056b90ae5", null],
            "mobile": ["43153f5d-e2d3-439f-b608-1afbae91ddf6", "PUBLIC"]
        },
        "it_systems": {
            "samAccountName": "fb2ac325-a1c4-4632-a254-3a7e2184eea7"
        }
    }
}

Hvor betydniningen af de enkelte felter er angivet højere oppe i dokumentationen. Felter som omhandler skolemdomænet er foreløbig sat via miljøvariable og er ikke inkluderet her, da ingen af skriveintegrationerne på dette tidspunkt undestøtter dette.

Det skal nu oprettes et lokalt afviklingsmiljø. Dette gøres ved at klone git projektet i en lokal mappe og oprette et lokal python miljø:

git clone https://github.com/OS2mo/os2mo-data-import-and-export
cd os2mo-data-import-and-export
python3 -m venv venv
source venv/bin/activate
pip install --upgrade pip
pip install os2mo_data_import/
pip install pywinrm[kerberos]

For at bekræfte at alt er på plads, findes et værktøj til at teste kommunikationen:

cd integrations/ad_integration
python test_connectivity.py

Hvis dette returnerer med ordet ‘success’ er integrationen klar til brug.

Anvendelse af kommondolinjeværktøjer

Følgende funktionaliteter har deres eget kommandolinjeværktøj som gør det muligt at anvende dem uden at rette direkte i Python koden:

  • ad_writer.py
  • ad_life_cycle.py
  • execute_ad_script.py
  • user_names.py

For user names kræves der dog en del forudsætninger som gør at kommandolinjeværktøjet ikke praksis har brugbar funktionalitet endnu.

ad_writer.py

Dette værktøj har følgende muligheder:

usage: ad_writer.py [-h]
                 [--create-user-with-manager MO_uuid |
                 --create-user MO_uuid |
                 --sync-user MO_uuid | --delete-user User_SAM |
                 --read-ad-information User_SAM |
                 --add-manager-to-user Manager_SAM User_SAM]
De forskellige muligheder gennemgås her en ad gangen:
  • –create-user-with-manager MO uuid

    Eksempel: python ad_writer-py –create-user-with-manager 4931ddb6-5084-45d6-9fb2-52ff33998005

    Denne kommando vil oprette en ny AD bruger ved hjælp af de informationer der er findes om brugeren i MO. De relevante felter i AD vil blive udfyld i henhold til den lokale feltmapning, og der vil blive oprettet et link til AD kontoen for lederen af medarbejderens primære ansættelse. Hvis det ikke er muligt at finde en leder, vil integrationen standse med en ManagerNotUniqueFromCprException.

  • –create-user MO_uuid

    Eksempel: python ad_writer-py –create-user 4931ddb6-5084-45d6-9fb2-52ff33998005

    Som ovenfor men i dette tilfælde oprettes der ikke et link til lederens AD konto.

  • –sync-user MO_uuid

    Eksempel: python ad_writer-py –sync-user 4931ddb6-5084-45d6-9fb2-52ff33998005

    Synkroniser oplysninger fra MO til en allerede eksisterende AD konto.

  • –delete-user User_SAM

    Eksempel: python ad_writer-py –delete-user MGORE

    Slet den pågældende AD bruger. Denne funktion anvendes hovedsageligt til tests, da et driftmiljø typisk vil have en mere kompliceret procedure for sletning af brugere.

  • –read-ad-information User_SAM

    Eksempel: python ad_writer-py –read-ad-information MGORE

    Returnere de AD oplysninger fra AD som integrationen i øjeblikket er konfigureret til at læse. Det er altså en delmængde af disse oplysninger som vil blive skrevet til MO af synkroniseringsværktøjet. Funktionen er primært nyttig til udvikling og fejlfinding.

  • –add-manager-to-user Manager_SAM User_SAM

    Eksempel: python ad_writer-py –add-manager-to-user DMILL MGORE

    Udfylder brugerens manager felt med et link til AD kontoen der hører til ManagerSAM.

ad_life_cycle.py

Dette værktøj kan afhængig af de valgte parametre oprette eller deaktivere AD-konti på brugere som henholdsvis findes i MO men ikke i AD, eller findes i AD, men ikke har aktive engagementer i MO.

::
usage: ad_life_cycle.py [-h]
[–create-ad-accounts] [–disable-ad-accounts] [–dry-run]

Betydningen af disse parametre angives herunder, det er muligt at afvilke begge synkroniseringer i samme kørsel ved at angive begge parametre.

  • –create-ad-accounts

    Opret AD brugere til MO brugere som ikke i forvejen findes i AD efter de regler som er angivet i settings-nøglen integrations.ad.write.create_user_trees.

  • –disable-ad-accounts

    Sæt status til Disabled for AD konti hvor den tilhøende MO bruge ikke længere har et aktivt engagement.

  • –dry-run

    Programmet vil ikke forsøge at opdatere sit billede af MO, en vil anvende den aktuelt cache’de værdi. Dette kan være nyttigt til udvikling, eller hvis flere integrationer køres umidelbart efter hinanden.

Det er værd at bemærke at brugerne som laves med ad_life_cycle ikke oprettes med relaterede data, de vil altså fremstå f.eks. uden adresser. Deres relaterede data kan tilførsel vha. ad_sync programmet.

execute_ad_script.py

Dette værktøj har følgende muligheder:

usage: execute_ad_script.py [-h]
                            [--validate-script Script name |
                            --execute-script Script name user_uuid]
De forskellige muligheder gennemgås her en ad gangen:
  • –validate-script Script_name

    Eksempel: python ad_writer-py –validate-script send_email

    Denne kommando vil lede efter en skabelon i scripts/send_email.ps_template og validere at skabelonen kun indeholder gyldige nøgleværdier. Hvis dette er tilfældet returneres sætningen “Script is valid” og ellers returneres en fejlbesked som beskriver hvilke ugyldige nøgler der er fundet i skabelonen.

  • –execute-script Script name user_uuid Eksempel: python execute_ad_script.py –execute-script send_email 4931ddb6-5084-45d6-9fb2-52ff33998005

    Denne kommando vil finde en skabelon i scripts/send_email.ps_template og først validere og derefter afvikle de med værdier taget fra brugen med uuid som angivet.

Import af AD OU til MO

Som en ekstra funktionalitet, er det muligt at anvende AD integrationens læsefaciliteter til at indlæse en bestemt OU fra AD’et til MO. Dette vil eksempelvis kunne anvendes hvis AD’et er autoritativ for eksterne konsulenter i kommunen og man ønsker, at disse personer skal fremgå af MOs frontend på trods af at de ikke importeres fra lønsystemet. Integrationen vil oprette ansættelsestypen ‘Ekstern’ og vil oprette alle brugere fra et på forhånd angivet OU som ansatte i MO. Det er en forudsætning, at disse brugere ikke har andre ansættelser i MO i forvejen. Hvis brugere fjernes fra OU’et vil de blive fjernet fra MO ved næste kørsel af integrationen.

I den nuværende udgave af integrationen, genkendes OU’et med eksterne brugere på, at dets navn indeholder ordene ‘Ekstern Konsulenter’, dette vil på sigt blive erstattet med konfiguration.

For at programmet kan afvikles, er det nødvendigt at sætte konfigurationsværdien integrations.ad.import_ou.mo_unit_uuid som angiver UUID’en på den enhed brugerne fra AD skal synkroniseres til. Hvis enheden ikke eksisterer i forvejen vil den blive oprettet ved første kørsel, så for en kommune som starter op med brug af denne integration, kan der blot angives et tilfældigt UUID.

Programmet hedder import_ad_group_into_mo.py og kan anvendes med et antal kommandolinjeparametre:

  • –create-or-update: Opretter og opdaterer bruger fra AD til MO.
  • –cleanup-removed-users: Fjerne MO brugere som ikke længere er konsulenter i AD.
  • –full-sync: Kører begge de to ovenstående operationer.

Exporters

CSV Exporters

This set of code allows to export the content of MO into a fixed set of csv-files containing various sub-sets of the content of MO.

Installation

The code contains a general exporter as well as code specific to various municipalities, the municipality-specfic code should only be run, if you are running MO from the corresponding municipality, since these exporters expects data specific to theses places.

The general code can be run directly from the folder with no installation required.

Requirements

The modules depend on mora_helper wich can be installed from the Helpers directory from the root of this repo: sudo pip3 install -e os2mo_helpers

Configuration

If MO is set up to use authentication, the exporter needs a valid service SAML token. This is read from the environment variable SAML_TOKEN.

Exported data

The general exporter will produce the following data-files:

  • alle_lederfunktioner_os2mo.csv
  • alle-medarbejdere-stilling-email_os2mo.csv
  • org_incl-medarbejdere.csv
  • adm-org-incl-start-og-stopdata-og-enhedstyper-os2mo.csv
  • tilknytninger.csv

Please note that these exports contain the same personal details as MO itself, and thus it is important to secure appropriate handling of the exported files.

Command line options

general_export.py accepts two command line parameters:

–root: uuid for the root org to export. If this parameter is not given, the deepest available tree will be assumed.

–threaded-speedup: If set to True, the program will start a full multithreaded read of all employees in MO. On most systems this will be significantly faster, but will result in a higher server load and a longer delay before the first export is finished.

–hostname: Hostname for the MO instance. Defaults to localhost.

Deployment

In order to run the exporter on a continuous basis (eg a nightly run) a cron job should be set up and SAML_TOKEN should be given a valid value.

To set up the cron job, find the uuid of the wanted root-unit and run this command from cron:

python3 general_export.py –hostname=localhost –root=<uuid>

unless the deployment is for one of the specific municipalities with a specific set of export code.

Eksport til plan2learn

Indledning

Denne eksport script bygger datafiler som via sftp kan sendes til plan2learn.

Koden består af to dele plan2learn.py som foretager eksporteringen fra MO til 5 csv-filer, samt ship_files.py, som via ftpes afleverer filerne til Play2Learn. Forbindelsesoplysninger på ftp-serveren angives i settings.json via tre nøgler:

  • exporters.plan2learn.host
  • exporters.plan2learn.user
  • exporters.plan2learn.password

Værktøjet har mulighed for at anvende to forskellige backends til at hente data: de kan hentes fra MO, eller direkte fra LoRa via LoRa-cache-mekanisme som også anvendes i andre eksportmoduler. LoRa-cache udgaven er langt den hurtigste og MO backenden er hovedsageligt bibeholdt for at kunne foretage en sammenligning mellem de to backends med henblik på fejlfinding.

Implementeringsstrategi

Der udarbejdes i alt 5 csv udtræk:

  • bruger.csv: Udtræk af alle nuværende og kendte fremtidigt aktive brugere i kommunen.
  • organisation.csv: Udtræk af alle organisationsenheder og deres relation til hinanden.
  • engagement.csv: Udtræk over alle nuværende og kendte fremtidige engagementer.
  • stillingskode.csv: Udtræk over alle aktive stillingsbetegnelser.
  • leder.csv: Udtræk over alle ledere i kommunen.

Brugerudtrækket

I dette udtræk eksporteres disse felter:

  • BrugerId: Brugerens uuid i MO
  • CPR: Brugerens cpr-nummer
  • Navn: Brugerens fulde navn, ikke opdelt i fornavn og efteranvn
  • E-mail: Hvis brugeren har en email i MO, angives den her.
  • Mobil: Hvis brugeren har en mobiltelefon i MO, angives den her.
  • Stilling: Stillingsbetegnelse for brugerens primærengagement. Hvis dette engagement har en stillingsbetegnelse skrevet i feltet extension_2 anvendes dette, og ellers anvendes stillingsbetegnelsen fra engagementet.

E-mail og mobiltelefon genkenes via bestemte klasse under adressetype, disse klasse er for nuværende hårdkodet direkte i python filen, men vil på sigt blive flyttet til settings.json.

Kun personer med ansættelsestype Timeløn eller Månedsløn inkluderes i udtrækket. Disse typer genkendes via en liste med de to uuid’er på typerne, for nuværende er listen hårdkoden direkte i python filen, men vil på sigt blive flyttet til settings.json.

Organisation

I dette udtræk eksporteres disse felter:

  • AfdelingsID: Afdelingens uuid i MO.
  • Afdelingsnavn: Afdelingens navn.
  • Parentid: uuid på enhedens forældreenhed.
  • Gade: Gadenavn
  • Postnr: Postnummer
  • By: Bynavn

Kun enheder på strukturniveau eksporteres. Dette foregår på den måde, at hvis enheden har et enhedsniveau (org_unit_level) som figurerer i nøglen integrations.SD_Lon.import.too_deep i settings.json vil enheden blive ignoreret.

Enheder som ikke har en gyldig adresse i MO, vil få angivet en tom streng for Gade, Postnr og By.

Rodenheden for organisationen vil have en tom streng som Parentid.

Engagement

I dette udtræk eksporteres disse felter:

  • BrugerId: Brugerens uuid i MO. Nøgle til Bruger -udtrækket.
  • AfdelingsId: Afdelingens uuid i MO. Nøgle til Organisation -udtrækket.
  • AktivStatus: Sættes til 1 for aktive engagementer, 0 for fremtidige.
  • StillingskodeId: uuid til engagements titel, som gemmes som en klasse under facetten engagement_job_function i MO. Nøgle til stillingskode.
  • Primær: 1 hvis engagementet er primært, ellers 0.
  • Engagementstype: Angiver om stillingen er måneds eller timelønnet.
  • StartdatoEngagement: Startdato hvis engagementet endnu ikke er startet

Kun bestemte typer engagementer eksporteres, dette vil normalt være timelønnede og månedslønnede. Disse genkendes via nøglen exporters.plan2learn.allowed_engagement_types i settings.json, som angiver en liste over uuid’er på de engagementstyper som skal eksporeres.

Engagmenter som tidligere har været aktive, men som nu er afsluttede, eksporteres ikke. Kendte fremtidige engagementer eksporteres med AktivStatus 0.

Stillingskode

I dette udtræk eksporteres disse felter:

  • StillingskodeID: uuid på den klasse i MO som holder stillingsbetegnelsne, nøgle til Engagement -udtrækket
  • AktivStatus: Angiver om stillingskoden anvendes. Der eksporteres kun akive stillingskoder, så værdien er altid 1.
  • Stillingskode: Læsbar tekstrepræsentation af stillingskoden (i modsæting til uuid’en).
  • Stillingskode#: I øjeblikket en Kopi af StillingskodeID.

Leder

I dette udtræk eksporteres disse felter:

  • BrugerId: Brugerens uuid i MO. Nøgle til Bruger -udtrækket.
  • AfdelingsID: Afdelingens uuid i MO. Nøgle til Organisation -udtrækket.
  • AktivStatus: Kun aktive ledere eksporteres, væriden er altid 1.
  • Titel: Lederens ansvarsområder.

Eksport til Actual State SQL database

Indledning

Denne eksport laver et dags-dato udtræk af MO og afleverer det i en SQL database.

For at opnå den nødvendige afviklingshastighed, tilgår eksporten data direkte fra LoRa hvor det er muligt at lave bulk udtræk, baseret på den kendte datamodel for OS2MO behandles de udtrukne data så SQL eksporten får et udseende som svarer til det man finder i MO.

Tabellerne er for den praktiske anvendeligheds skyld ikke 100% normaliserede, der vil således for alle klasser altid være både en reference til primærnøglen for klassen plus en tekstrepræsentation, så det er muligt at aflæse tabellen uden at skulle foretage et join mod tabellen klasser for alle opslag.

Implementerigen er foretaget ved hjælp af værktøjet SQLAlchemy, som sikrer at det er muligt at aflevere data til en lang række forskellige databasesystemer, det er desuden muligt at køre hele eksporten mod en flad SQLite fil som muliggør eksportering helt uden en kørende databaseserver.

Konfiguration

For at anvende eksporten er det nødvendigt at oprette et antal nøgler i settings.json:

  • exporters.actual_state.manager_responsibility_class: UUID på det lederansvar, som angiver at en leder kan nedarve sin lederrolle til enheder dybere i organisationen.
  • exporters.actual_state.type: Typen af database, i øjeblikket understøttes SQLite, Mysql, MS-SQL, samt MS-SQL-ODBC. flere kan tilføjes efter behov. MS-SQL-driveren er på vej ud og er erstattet af MS-SQL-ODBC i nyere installationer.
  • exporters.actual_state_historic.type: Som ovenfor, men for historisk eksport.
  • exporters.actual_state.user: Brugernavn for sql bruger.
  • exporters.actual_state.password: Password til sql bruger.
  • exporters.actual_state.db_name: Navn på databasen for actual state eksport.
  • exporters.actual_state_historic.db_name: Navnet på databasen for historisk eksport.
  • exporters.actual_state.host: Hostnavn på SQL-serveren.

For typen SQLite kan user, password og host være tomme felter.

Eksport af historik

MO/LoRa er en historisk database hvor de fleste objekter kan have forskellige værdier på forskellige tidspunkt, såkaldte virkningstider. SQL-eksporten vil som udgangspunkt eksportere de aktuelt gyldige værdier, men det er også muligt at foretage en komplet eksport af alle gyldigheder.

Kommandolinjeværktøj

En eksport kan startes fra kommandolinjen med følgende parametre:
  • --resolve-dar: Hvis denne parameter er sat, vil eksporten forsøge at slå MOs DAR uuid’er op, så adressen også eksporteres i klar tekst. Hvis datasættet indeholder mange forskellige adresser, vil det betyde en betydelig forøgelse af kørselstiden.
  • --historic: Hvis denne parameter er sat, vil der blive foretaget en fuld eksport af både fortidige, nutidige og fremtidige rækker. Dette vil betyde, at en række beregnede parametre ikke vil komme med i datasættet.
  • --force-sqlite: Denne parameter vil betyde at exporters.actual_state.type i settings.json vil blive ignoreret, og en SQLite-fil vil blive eksporteret.
  • --use-pickle: Ingen opslag vil blive foretaget i LoRa, udtrækket vil baseres på cache-filer fra sidste gennemløb, mest anvendeligt til udvikling.

Modellering

Langt hovedparten af de data som eksporteres kan betragtes som rene rådata, der er dog nogle få undtagelser, hvor værdierne er fremkommet algoritmisk, disse værdier beregnes kun ved dags-dato eksport, ved fuld eksport af historiske værdier, er felterne tomme:

  • enheder.organisatorisk_sti: Angiver den organisatoriske sti for en enhed beregnet ved at gå baglæns gennem enhedstræet og tilføje et \-tegn mellem hver enhed. Eksempel: Basildon Kommune\Kunst & Kultur\Musiktilbud\Øvelokaler.
  • enheder.fungerende_leder: I MO er en leder modeleret som en organisationfunktion som sammenkæder en person med en enhed. Der er ikke noget krav om at alle enheder har en lederfunktion pegende på sig, og der vil derfor være enheder som ikke figurerer i tabellen ledere. For disse enheder er det muligt algoritmisk at bestemme en leder ved at gå op i træet indtil der findes en leder med passende lederansvar. Dette felt indeholder resultatet af denne algoritme.
  • adresser.værdi: For alle adressetyper, undtaget DAR adresser, er dette felt taget direkte fra rådata. For DAR-adresser, er rådata en UUID og ikke en tekststreng, i dette tilfælde indeholder dette felt resultatet af et opsalg mod DAR, og den egentlige rådata (UUID’en) befinder sig i feltet dar_uuid.
  • engagementer.primærboolean: Beregnes ved at iterere hen over alle engagementer for en bruger, det engagement som har det højeste scope på sin primærklasse vil blive markeret som primær, alle andre vil blive markeret som ikke-primært.

Eksporterede tabeller

Eksporten producerer disse tabeller, indholdet af de enkelte tabeller gennemgås systematisk i det følgende afsnit.

  • facetter
  • klasser
  • brugere
  • enheder
  • adresser
  • engagementer
  • roller
  • tilknytninger
  • orlover
  • it_systemer
  • it_forbindelser
  • ledere
  • leder_ansvar
  • KLE

facetter

  • uuid: Facettens uuid, primærnøgle for tabellen.
  • bvn: Brugervendt nøgle for facetten.

Facetter i MO har ikke nogen titel, og ikke nogen historik.

klasser

  • uuid: Klassens uuid, primærnøgle for tabellen.
  • bvn: Brugervendt nøgle for klassen.
  • titel: Klassens titel, det er denne tekst som vil fremgå af MOs frontend.
  • facet_uuid: Reference til primærnøglen i tabellen facetter.
  • facet_bvn: Den brugervendte nøgle som knytter sig til klassens facet.

Klasser regnes af MO til at have evig virkning og eksporteres derfor altid med dags-dato værdi, også i et historisk eksport.

brugere

  • uuid: Brugerens uuid, primærnøgle for tabellen.
  • fornavn: Brugerens fornavn.
  • efternavn: Brugerens efternavn.
  • kaldenavn_fornavn: Fornavnet på brugerens kaldenavn.
  • kaldenavn_efternavn: Efternavnet på brugerens kaldenavn.
  • cpr: Brugerens cpr-nummer.
  • startdato: Startdato for denne rækkes gyldighed.
  • slutdato: Slutdato for denne rækkes gyldighed.

enheder

  • uuid: Enhedens uuid, primærnøgle for tabellen.
  • navn Enhedens navn.
  • forældreenhed_uuid: Reference til primærnøglen for forælderenheden.
  • enhedstype_uuid: Enhedstypen, reference til primærnøglen i tabellen
  • enhedstype_titel: Titel på enhedstypens klasse. klasser.
  • enhedsniveau_uuid: Enhedsniveau, dette felt anvendes normalt kun af kommuner, som anvender SD som lønsystem. Reference til primærnøglen i tabellen klasser.
  • enhedsniveau_titel: Titel på klassen for enhedsniveau.
  • organisatorisk_sti: Enhedens organisatoriske placering, se afsnit om Modellering.
  • leder_uuid: Reference til primærnøglen for det lederobjet som er leder af enheden. Informationen er teknisk set redundant, da den også fremkommer ved et join til tabellen ledere, men angives også her som en bekemmelighed.
  • fungerende_leder_uuid: Reference til primærnøglen for nærmeste leder af enheden. Hvis enheder har en leder, vil dette være det samme som leder. Feltet er et afledt felt og findes ikke i rådata, se afsnit om Modellering.
  • startdato: Startdato for denne rækkes gyldighed.
  • slutdato: Slutdato for denne rækkes gyldighed.

adresser

Adresser er i MO organisationfunktioner med funktionsnavnet Adresse.

  • uuid: Adressens (org-funk’ens) uuid, primærnøgle for tabellen
  • bruger_uuid: Reference til primærnøglen i tabellen brugere. Hvis adressen er på en enhed, vil feltet være blankt.
  • enhed_uuid: Reference til primærnøglen i tabellen enheder. Hvis adressen er på en bruger, vil feltet være blankt.
  • værdi: Selve adressen, hvis adressen er en DAR-adresse, vil dette felt indeholde en tekstrepræsentation af adressen.
  • dar_uuid: DAR-uuid’en som liger bag opslaget som fremgår af værdi_tekst. Blankt hvis ikke adressen er en DAR-adresse.
  • adressetype_uuid: Adressetypen, reference til primærnøglen i tabellen klasser.
  • adressetype_scope: Adressens overordnede type (omfang), eksempelvis Telefon eller P-nummer.
  • adressetype_titel: Titlen på adressetypens klasse.
  • synlighed_uuid: Synlighedstype, reference til primærnøglen i tabellen klasser.
  • synlighed_titel: Titlen på synlighedstypens klasse.
  • startdato: Startdato for denne rækkes gyldighed.
  • slutdato: Slutdato for denne rækkes gyldighed.

engagementer

Engagementer er i MO organisationfunktioner med funktionsnavnet Engagement.

  • uuid: Engagementets (org-funk’ens) uuid, primærnøgle for tabellen.
  • bruger_uuid: Reference til primærnøglen i tabellen brugere.
  • enhed_uuid: Reference til primærnøglen i tabellen enheder.
  • bvn: Engagementets brugervendte nøgle. Dette vil i de fleste tilfælde være ansættelsesnummeret i lønsystemet.
  • arbejdstidsfraktion: Angiver den registrerede arbejdstidsfraktion for engagementet.
  • engagementstype_uuid: Engagementstypen, reference til primærnøglen i tabellen klasser.
  • engagementstype_titel: Titlen på engagementstypeklassen.
  • primærtype_uuid: Engagementets primærtype, reference til primærnøglen i tabellen klasser.
  • primærtype_titel: Titlen på primærtypetypeklassen.
  • stillingsbetegnelse_uuid: Engagementets stillingsbetegnelse, reference til primærnøglen i tabellen klasser.
  • job_function_titel: Titlen på klassen for stillingsbetegnelse.
  • primær_boolean: Boolean som angiver om engagementet er brugerens primære engagement, se afsnit om beregnede felter
  • udvidelse_1: Første af 10 fritekstfelter på MOs engagementer
  • udvidelse_10: Sidste af 10 fritekstfelter på MOs engagementer
  • startdato: Startdato for denne rækkes gyldighed.
  • slutdato: Slutdato for denne rækkes gyldighed.

roller

Roller er i MO organisationfunktioner med funktionsnavnet Rolle.

  • uuid: Rollens (org-funk’ens) uuid, primærnøgle for tabellen.
  • bruger_uuid: Reference til primærnøglen i tabellen brugere.
  • enhed_uuid: Reference til primærnøglen i tabellen enheder.
  • rolletype_uuid: Rolletypen, reference til primærnøglen i tabellen klasser.
  • rolletype_titel: Titlen på klassen for rolletypen.
  • startdato: Startdato for denne rækkes gyldighed.
  • slutdato: Slutdato for denne rækkes gyldighed.

tilknytninger

Tilknytninger er i MO organisationfunktioner med funktionsnavnet Tilknytning.

  • uuid: Tilknytningens (org-funk’ens) uuid, primærnøgle for tabellen.
  • bvn: Tilknytningens brugervendte nøgle.
  • bruger_uuid: Reference til primærnøglen i tabellen brugere.
  • enhed_uuid`: Reference til primærnøglen i tabellen ``enheder.
  • tilknytningstype_uuid: Tilknytningstypen, reference til primærnøglen i tabellen klasser.
  • tilknytningstype_text: Titlen på klassen for tilknytningstypen.
  • startdato: Startdato for denne rækkes gyldighed.
  • slutdato: Slutdato for denne rækkes gyldighed.

orlover

Orlover er i MO organisationfunktioner med funktionsnavnet Orlov.

  • uuid: Orlovens (org-funk’ens) uuid, primærnøgle for tabellen.
  • bvn: Brugervendt nøgle for orloven.
  • bruger_uuid: Reference til primærnøglen i tabellen brugere.
  • orlovstype_text: Titlen på klasse for orlovstypen.
  • orlovstype_uuid: Orlovstypen, reference til primærnøglen i tabellen klasser.
  • startdato: Startdato for denne rækkes gyldighed.
  • slutdato: Slutdato for denne rækkes gyldighed.

it_systemer

  • uuid: IT-systemets uuid, primærnøgle for tabellen.
  • navn: IT-systemets navn.

IT-systmer regnes af MO til at have evig virkning og eksporteres derfor altid med dags-dato værdi, også i et historisk eksport.

it_forbindelser

IT-forbindelser er i MO organisationfunktioner med funktionsnavnet IT-system.

IT-forbindeler dækker over en sammenkædningen mellem et IT-system og enten en enhed eller en bruger. Hvis forbindelsen er til en bruger, vil sammenkædningen indeholde brugerens brugernavn i det pågældende system. Hvis forbindelsen er til en enhed, skal den tolkes i betydningen, at dette IT-system er i anvendelse i den pågældende enhed, i dette tilfælde vil der normalt ikke være brugernavn på forbindelsen.

  • uuid: IT-forbindelsens (org-funk’ens) uuid, primærnøgle for tabellen.
  • it_system_uuid: Reference til primærnøglen i tabellen it_systemer
  • bruger_uuid: Reference til primærnøglen i tabellen brugere. Hvis it-forbindelsen er på en enhed, vil feltet være blankt.
  • enhed_uuid: Reference til primærnøglen i tabellen enheder.
  • brugernavn: Brugerens brugernavn i IT-systemet. Normalt blank for forbindelser til enheder.
  • startdato: Startdato for denne rækkes gyldighed.
  • slutdato: Slutdato for denne rækkes gyldighed.

ledere

  • uuid: Lederrollens (org-funk’ens) uuid, primærnøgle for tabellen.
  • bruger_uuid: Reference til primærnøglen i tabellen brugere.
  • enhed_uuid: Reference til primærnøglen i tabellen enheder.
  • ledertype_titel: Titlen på klassen for ledertypen.
  • ledertype_uuid: Klassen for ledertypen, reference til primærnøglen i tabellen klasser.
  • niveautype_titel: Titlen på klassen for lederniveau.
  • niveautype_uuid: Klassen for lederniveau, reference til primærnøglen i tabellen klasser.
  • startdato: Startdato for denne rækkes gyldighed.
  • slutdato: Slutdato for denne rækkes gyldighed.

leder_ansvar

Lederansvar er i MO ikke et selvstændigt objekt, men er modelleret som en liste af klasser som tilknyttes en lederrolle.

  • id: Arbitrært løbenummer, denne tabel har ikke har nogen naturlig primærnøgle.
  • leder_uuid: Reference til primærnøglen i tabellen ledere.
  • lederansvar_uuid: Klassen for lederansvar, reference til primærnøglen i tabellen klasser.
  • lederansvar_titel: Titlen på klassen for lederansvar.
  • startdato: Startdato for denne rækkes gyldighed.
  • slutdato: Slutdato for denne rækkes gyldighed.

KLE

KLE opmærkning er i MO ikke selvstændige objekter, men er modelleret som en binding imellem to klasser, nemlig KLE-Aspekt (f.eks. udførende) og KLE-numre.

  • id: Arbitrært løbenummer, primærnøgle for tabellen.
  • uuid: KLE-bindingens uuid.
  • enhed_uuid: Reference til primærnøglen i tabellen enheder.
  • kle_aspekt_uuid: Klassen for KLE-aspekt, reference til primærnøglen i tabellen klasser.
  • kle_aspekt_titel: Titlen på KLE-aspektets klasse.
  • kle_nummer_uuid: Klassen for KLE-nummer, reference til primærnøglen i tabellen klasser.
  • kle_nummer_titel: Titlen på KLE-nummerets klasse.
  • startdato: Startdato for denne rækkes gyldighed.
  • slutdato: Slutdato for denne rækkes gyldighed.

Actual state til cron-jobs

Jobs kører generelt hurtigere, hvis de anvender en actual state database, end hvis de anvender rest-kald imod os2mo.

Derfor findes muligheden for at slå et job til, som genererer en actual state database i SQLite-format til brug for cron-jobs.

For at anvende denne eksport er det nødvendigt at oprette enkelt nøgle i settings.json:

  • lc-for-jobs.actual_db_name: Navnet på filen (eksporten tilsætter selv ‘.db’ til navnet)

Databasen vil kun være skrivbar imens den bliver genereret.

Eksport til EMUS

Indledning

Dette program eksporterer xml som kan indlæses i emus - også kaldet musskema.dk og sender dem efter endt programafvikling 2 steder hen:

  • Til ‘exports’-directoriet. som kan nås igennem OS2MO’s frontend
  • Via sftp til musskema.dk.

Programkomponenter

Programmet er udført i to afdelinger, opstået i forbindelse med implenteringen, hvor vi staertede med at udveksle xml-filer uden automatiseret sftp.

  • viborg_xml_emus.py er den komponent, der skaber xml-outputtet
  • viborg_xml_emus_sftp.py er den komponent, der sørger for at sende outputtet til de to destinationer

Indhold af udtrækket

Udtrækket indeholder

  • Medarbejderes engagementer
  • Organisatoriske enheder
  • Ledere

Medarbejderes engagementer

Denne del af udtrækket indeholder for hver ansættelse start, slut, cpr, navn, adresse, telefon, engagementstype, tjenestenummer, email og telefon og brugernavn i valgte IT-system’ Herudover er der et client-felt, som er 1 for medabejdere i denne del af udtrækket.

Telefon og email udlades hvis scope er ‘SECRET’

Nøglen, employee_id, som overføres er tjenestenr.

Timelønnede medarbejdere er ikke med i udtrækket.

I settings.json vedligeholdes en liste af frasorterede job-funktioner, som også fjernes fra udtrækket

Organisatoriske enheder

Denne del af udtrækket indeholder for hver afdeling navn, adresse, telefonnummer, leder og afdelingens tidsgyldighed.

Organisatoriske enheder, som kun har timelønnede medarbejdere er ikke med i udtrækket.

Ledere

Ledere ligner medarbejderne, men har anderledes værdier i client og employee_id. Feltet client er for ledere hårdkodet til 540. Nøglen, employee_id, som overføres er lederens eget , ikke engagementets uuid.

Telefon og email udlades hvis scope er ‘SECRET’

Lederengagementer uden en tilknyttet person er ikke med i udtrækket.

Ledereengagementer, som ikke er af EMUS_RESPONSIBILITY_CLASS kommer ikke med i udtrækket.

Styring af udtrækket

Programmet er afhængig af følgende indstillinger i settings.json

  • emus.discard_job_functions angiver jobfunktioner, der skal springes over
  • emus.manager_responsibility_class angiver den leder-klasse man vil overføre
  • emus.outfile_name default emus_filename.xml er det filnavn viborg_xml_emus.py kan skrive til
  • emus.recipient angiver den bruger, som man skal sende til. Dette giver kun mening med sftp på serviceplatformen
  • emus.sftp_host er typisk sftp.serviceplatformen.dk, men til test kan en anden anvendes
  • emus.sftp_key_passphrase angiver password til ovenst. nøgle
  • emus.sftp_key_path angiver den nøgle, man anvender got at forbinde sig til sftp-serveren
  • emus.sftp_user angiver den sftp-user, man forbinder som
  • emus.userid_itsystem angiver hvilket IT-system, man tager brugernavnet fra, default er Active Directory.
  • emus.engagement_types angiver en liste af engagementstypeklasser, som kommer med (UUIDer på for eksempel: “Medarbejder (månedsløn)”)
  • emus.phone.priority indsnævrer valg af tlfnr. til en prioriteret liste af addresse-klasser med scope PHONE, som må bruges
  • emus.email.priority indsnævrer valg af email til en prioriteret liste af addresse-klasser med scope EMAIL, som må bruges
  • mora.admin_top_unit angiver roden af det organisatoriske træ, der skal overføres
  • mora.base styrer hvilken OS2MO, der tilgås
  • mora.folder.query_export angiver det sted, hvor kopien af rapporten skal lægges - dette skal være det output-dir, som kan nås igennem OS2MO.

Programmet kan styres af følgende environment-variable:

  • LOG_LEVEL kan anvendes til at få mere log ud under afviklingen, hvis man sætter den til strengen DEBUG

Import tool

OS2MO data import tool

A small higher level utility for os2mo data import.

The utility provides functionality to create organisation units and employees and to create all of the types to which these can be assigned.

Additonally the utility can be used to insert data objects into a running instance of os2mo.

Installing

Install the os2mo_data_import package as follows:

# Checkout the mora source repository
https://github.com/OS2mo/os2mo-data-import-and-export

# Navigate to the local copy of the repository
cd /path/to/os2mo-data-import-and-export

# Install package with pip
pip install -e os2mo_data_import

Getting started

The main entry point (for most use cases) is the Organisation class, which functions as a wrapper for all the sub classes.

# Imports
from os2mo_data_import import ImportHelper

# Init helper
os2mo = ImportHelper(create_defaults=True, store_integration_data=True)

# Add organisation
os2mo.add_organisation(
    identifier="Magenta Aps",
    user_key="Magenta",
    municipality_code=101
)

Organisation Units

Before organisation units can be created, a type for the unit must be added:

# Add klasse with reference to facet "org_unit_type"
os2mo.add_klasse(
    identifier="Hovedenhed",
    facet_type_ref="org_unit_type",
    user_key="D1ED90C5-643A-4C12-8889-6B4174EF4467",
    title="Hovedenhed"  # This is the displayed value
)

Now an organisation unit can be added with “org_unit_type_ref” referencing the user defined identifier of the newly created unit type by name:

# Root unit: Magenta
# Belongs to unit type: "Hovedenhed"
os2mo.add_organisation_unit(
    identifier="Magenta",
    name="Magenta Aps",
    type_ref="Hovedenhed",  # Reference to the unit type
    date_from="1986-01-01"
)

Organisation unit “Magenta” is a root unit in this example. To add children/sub units, “Magenta” must be referenced as parent:

# Add unit type "Afdeling"
os2mo.add_klasse(
    identifier="Afdeling",
    facet_type_ref="org_unit_type",
    user_key="91154D1E-E7CA-439B-B910-D4622FD3FD21",
    title="Afdeling"
)

# Add sub unit "Pilestræde"
os2mo.add_organisation_unit(
    identifier="Pilestræde",
    type_ref="Afdeling",  # This unit is of type: Afdeling
    parent_ref="Magenta",  # Sub unit of/Belongs to Magenta
    date_from="1986-01-01"
)

Optional data or “details” can be associated with an organisation unit.

Note

At least 2 “Klasse” objects must be created, an object for the primary phone number and an object for the primary mailing address (residence).

The validation in the (os2mo) frontend application requires:

The user_key on the the primary phone number object must be specified as “PhoneUnit”

The user_key on the primary mail address object must be specified as “AddressMailUnit”

Hence either the “identifier” or the “user_key” must be set to:

  • PhoneUnit
  • AddressMailUnit

(The “user_key” is derived from the value of the “identifier if not explicitly set)

See the example below:

# Add klasse type "AdressePost"
# Which belongs to facet type "org_unit_address_type"

# user_key is not explicitly set, identifier must be "AddressMailUnit"
os2mo.add_klasse(
    identifier="AddressMailUnit",
    facet_type_ref="org_unit_address_type",
    title="Adresse",
    scope="DAR",
    example="<UUID>"
)

# Add klasse type "Telefon"
# Which belongs to facet type "org_unit_address_type"

# user_key is set to "PhoneUnit", hence the identifier can be anything
os2mo.add_klasse(
    identifier="Telefon",
    facet_type_ref="org_unit_address_type",
    user_key="PhoneUnit",
    title="Tlf",
    scope="PHONE",
    example="20304060"
)

# Add "AdressePost" detail to the unit "Magenta"
os2mo.add_address_type(
    organisation_unit="Magenta",
    value="0a3f50c4-379f-32b8-e044-0003ba298018",
    type_ref="AdressePost",
    date_from="1986-01-01"
)

# Add "Telefon" detail to the unit "Magenta"
os2mo.add_address_type(
    organisation_unit="Magenta",
    value="11223344",
    type_ref="Telefon",
    date_from="1986-01-01",
)

Employees

Employees are not directly attached to an organisation unit, but can have a job function which is linked to a unit.

Create employees first:

os2mo.add_employee(
    identifier="Susanne Chæf",
    cpr_no="0101862233"
)

os2mo.add_employee(
    identifier="Odin Perskov",
    cpr_no="0102862234"
)

Job function

Add the job function types:

# Job: CEO ("Direktør")
os2mo.add_klasse(
    identifier="Direktør",
    facet_type_ref="engagement_type",
    user_key="Direktør",
    title="Direktør"
)

# Job: Projectmanager ("Projektleder")
os2mo.add_klasse(
    identifier="Projektleder",
    facet_type_ref="engagement_type",
    user_key="Projektleder",
    title="Projektleder"
)

Add job functions to the newly created employees with the “add_type_engagement” method:

# Susanne Chæf is CEO
os2mo.add_engagement(
    employee="Susanne Chæf",
    organisation_unit="Magenta",
    job_function_ref="Direktør",
    engagement_type_ref="Ansat",
    date_from="1986-01-01"
)

# Odin Perskov is projectmanager
os2mo.add_engagement(
    employee="Odin Perskov",
    organisation_unit="Pilestræde",
    job_function_ref="Projektleder",
    engagement_type_ref="Ansat",
    date_from="1986-02-01"
)

Association

In this example the employee “Odin Perskov” is an external consultant, and to reflect this an association type can be assigned:

os2mo.add_klasse(
    identifier="Ekstern Konsulent",
    facet_type_ref="association_type",
    user_key="F997F306-71DF-477C-AD42-E753F9C21B42",
    title="Ekstern Konsulent"
)

# Add the consultant association to "Odin Perskov":
os2mo.add_association(
    employee="Odin Perskov",
    organisation_unit="Pilestræde",
    job_function_ref="Projektleder",
    association_type_ref="Ekstern Konsulent",
    address_uuid="0a3f50c4-379f-32b8-e044-0003ba298018",
    date_from="1986-10-01"
)

In the following example an address is assigned to employee “Odin Perskov”. For residential addresses, valid UUID’s are used to reference an address from the “Danish registry of addresses” (DAR):

# Add address type "AdressePostEmployee"
os2mo.add_klasse(
    identifier="AdressePostEmployee",
    facet_type_ref="employee_address_type",
    user_key="2F29C717-5D78-4AA9-BDAE-7CDB3A378018",
    title="Adresse",
    scope="DAR",
    example="<UUID>"
)

# Detail AdressePostEmployee assigned to "Odin Perskov"
os2mo.add_address_type(
    employee="Odin Perskov",
    value="0a3f50a0-ef5a-32b8-e044-0003ba298018",
    type_ref="AdressePostEmployee",
    date_from="1986-11-01",
)

Roles

To add a role type:

# A role as contact for external projects
os2mo.add_klasse(
    identifier="Nøgleansvarlig",
    facet_type_ref="role_type",
    user_key="0E078F23-A5B4-4FB4-909B-60E49295C5E9",
    title="Nøgleansvarlig"
)

# Role assigned to "Odin Perskov"
os2mo.add_role(
    employee="Odin Perskov",
    organisation_unit="Pilestræde",
    role_type_ref="Nøgleansvarlig",
    date_from="1986-12-01"
)

It systems

Generic IT systems can be created and assigned to employees with a specified “user_key”, which functions as a reference to a username, pin code etc.:

# Create IT system: Database
  os2mo.new_itsystem(
      identifier="Database",
      system_name="Database"
  )

  # Assign access to the database
  # with username "odpe@db"
  os2mo.join_itsystem(
      employee="Odin Perskov",
      user_key="odpe@db",
      itsystem_ref="Database",
      date_from="1987-10-01"
  )

Manager type, level and responsibilities

In order to assign employees as managers to an organisation unit, the following types must be created:

  • manager type
  • manager level
  • A type for each responsibility

Create manager type:

os2mo.add_klasse(
    identifier="Leder",
    facet_type_ref="manager_type",
    user_key="55BD7A09-86C3-4E15-AF5D-EAD20EB12F81",
    title="Virksomhedens direktør"
)

Create manager level:

os2mo.add_klasse(
    identifier="Højeste niveau",
    facet_type_ref="manager_level",
    user_key="6EAA7DA7-212D-4FD0-A068-BA3F932FDB10",
    title="Højeste niveau"
)

Create several responsibilities:

os2mo.add_klasse(
    identifier="Tage beslutninger",
    facet_type_ref="responsibility",
    user_key="A9ABDCCB-EC83-468F-AB7D-175B95E94956",
    title="Tage beslutninger"
)

os2mo.add_klasse(
    identifier="Motivere medarbejdere",
    facet_type_ref="responsibility",
    user_key="DC475AF8-21C9-4112-94AE-E9FB13FE8D14",
    title="Motivere medarbejdere"
)

os2mo.add_klasse(
    identifier="Betale løn",
    facet_type_ref="responsibility",
    user_key="0A929060-3392-4C07-8F4E-EF5F9B6AFDE2",
    title="Betale løn"
)

Assign the manager position of Magenta to “Susanne Chæf”:

os2mo.add_manager(
    employee="Susanne Chæf",
    organisation_unit="Magenta",
    manager_type_ref="Leder",
    manager_level_ref="Højeste niveau",
    responsibility_list=["Tage beslutninger", "Motivere medarbejdere", "Betale løn"],
    date_from="1987-12-01",
)

Preservation of UUIDs

If the system to be imported into MO contains UUIDs that should be preserved in MO, it is possible to import the UUIDs for employees, organisational units, classes and classifications. This is achieved by adding an extra uuid argument when creating the object, eg:

os2mo.add_klasse(
    identifier="Betale løn",
    facet_type_ref="responsibility",
    uuid="195da2b6-e648-4bdc-add1-e22654996997",
    user_key="0A929060-3392-4C07-8F4E-EF5F9B6AFDE2",
    title="Betale løn"
)

Continuous integration

It is possible to run the importer in a mode where the internal identifiers will be stored in the special field ‘integration_data’ in LoRa. This identifier will be recognized upon the next import and the object will be re-imported in contrast to being created again. In effect this will turn the importer into a one-way integration of the imported system.

Example

If a “real” os2mo application is available, a practial example is provided with contains similar import data as the given examples above.

Feel free to run the “import_example.py” included in the repository:

Example: $os2mo-data-import-and-export/os2mo_data_import/import_example.py

Run example:

cd os2mo_data_import
python import_example.py

Reference

For more information on the os2mo project, please refer to the official documentation.

Read the docs: https://os2mo.readthedocs.io

Known Issues

Currently it is not possible to assign “Leave” (e.g. various types of leave of absence).

This issue is related to the validation of type assignments.

Dummy data creator

This application will generate tree structure containing a mock-up of a Danish municipality. The organisation is randomized but modelled to be somewhat realistic. The model is provided with a municipality code and this information is used to provided addresses and also to create fictional schools and kindergartens based on the postal areas in the municipality.

Classification

A number of classes are provided, they are partly used to create the fictional users and units, but also to allow the user interface to work in a reasonable realistic way after the initial creation of the structure.

Unit properties

In the default state, all units will have exactly on manager and a number of users. The number of users will on average be higher in the deeper parts of the tree. A scale factor is provided to allow for as few or as many users as wanted. A scale factor of one, will create a very small tree, possible some units will have no employees at all. A scale factor of 4 will yield an organization of ~700-800 users.

User properties

In the default configuration, all users will have exactly one engagement, which will have high probability to be currently active. A smaller fraction of the engagements will be either in the past or in the future, this allows for tests and demonstrations of the bitemporality. All uses are equipped with a Danish address (given as DAR uuid as well as plain text) and an email.

The users will have a certain probability to have a role, no users will have more than one role. Also, the users will have probability to be associated with another unit.

Most, but not all, users will have at least one account in an IT-system.

Performance stress test

The two main functions create_org_func_tree and add_users_to_tree both takes arguments that will produce a very large but much less realistic, dataset. The main purpose of this is to create datasets that can be used for performance tests.

Specifically, if create_org_func_tree is call with org_size set to Size.Large, the structure will contain 25 copies of each school. If add_users_to_tree is called with multiple_employments set to True, each employee will have a large number of engagements, typically both in the past and in the future, and for most employees also in the present time.

Data structure

The data is stored in an AnyTree data-structure. The tree will currently always have either one root (default name root), or two roots, employees will always be leafs in the tree, some OUs might possibly have no employees in which case a unit is a leaf. All nodes have a type that can be either ou or user.

The data is read by traversing the tree. The manager field contains a list of responsibilities if the person is a manager, otherwise an empty list.

if node.type == 'ou':
    print(node.name)  # Name of the ou
    print(node.adresse['dar-uuid'])
    if node.parent:
        print(node.parent.key)  # Key for parent unit, root will have no parent

        if node.type == 'user':
            print(node.name)  # Name of the employee
            print(node.parent.key) # Key to the users unit
            user = node.user  # All unser information is here
            print(user[0]['cpr']) # user_key and cpr are identical in all elements
            print(user[0]['brugervendtnoegle'])
            for engagement in user: # An element for each engagement
                print(engagement['adresse']['dar-uuid'])
                print(engagement['email'])
                print(engagement['telefon'])
                print('engagement['role']))
                print(engagement['association'])
                print(engagement['manager'])
                print('From: {}. To: {}'.format(engagement['fra'],
                                                engagement['til']))

Importing into MO

If the user should want to import the structure into MO, this can be done with the script populate_mo.py.

Integration abstraction

A small library to easy the use of the integrationsdata field in LoRa.

The utility provides functionality to read and write fields from the integration data stored with an object, while taking care of not overwriting other keys stored by other integrations on the same object.

The utility also provides basic functionality to find objects based on their integration data.

Usage

Import the utility, eg:

from integration_abstraction.integration_abstraction import IntegrationAbstraction

The tool takes parameters for system_name and end_marker. system_name is the name of the key that will be used for the current session, the utility will take of abstracting away the underlying json-structure that is actually stored in the integrationsdata field, and will only presnet the user with the values associated with the key chosen by system_name.

end_marker is the value that is appeded to all values to ensure that it is possible to uniquely find objects despite the fact that structured search is not avaiable for integration data in LoRa. The value defaults to STOP. If this word could potentially be bart of the actual stored value, another end_marker should be chosen.

Writing and reading Integration Data

To write integration data to an object:

mox_base = 'http://localhost:8080'
resource = '/klassifikation/facet'
uuid = '00000000-0000-0000-0000-000000000001'

set_value = 'Rose Bowl 101'
ia = IntegrationAbstraction(mox_base, 'AD', 'STOP')
ia.write_integration_data(resource, uuid, set_value)

The data will be written to the objects integrations_data field while all other keys will be left untouched.

To read back he value:

read_value ia.write_integration_data(resource, uuid)

Complex data

It is to some degree possible to store more complex data structures. The data is stored as JSON, and thus dictionaries can be stored, as long as keys are strings:

set_value = {'a': 'klaf', 'b': 3, 'c': {'a': 1, 'b': 2, '5': {'def': 9}}}
ia.write_integration_data(resource, uuid, set_value)

The values must be json-serializable and thus it is not possible to store more complex structures like Python pickle objects.

Searching

It is possible to find objects based on their integration data value, provided that the vaulue does not contain characters that are considred special by the underlying search engnine in LoRa(see https://github.com/magenta-aps/mox/blob/95adfd192a729d6a82b08b2188dbda77522b881b/doc/dev/wildcards.rst), ie avoid characters such as ‘%’, ‘&’, ‘’ and ‘_’ if the object should be found in a search.

It is not possible to easily find more complex objects like dictionaries in a search.

To find a object with integration data value ‘AndyFl’:

value = 'AndyFl'

# Write the value
ia.write_integration_data(resource, uuid, value)

# Find the object
uuid = ia.find_object(resource, value)

Other tools

Tools

Tools indeholder scripts primært beregnet til at køre natlige jobs og restore af data efter fejlede jobs.

  • job-runner.sh - excutable beregnet til at blive kaldt fra crontab uden parametre
  • clear_mox_tables.py - beregnet til at tømme os2mo’s tabeller ved nyindlæsning
  • cron-restore.sh - beregnet til at restore OS2MO til før den kørsel, som backuppen er taget efter
  • moxklas.sh - beregnet til at oprette klasser i LORA - udenom OS2MO ved specialle behov
  • prefixed_settings.sh - beregnet til at eksportere settings fra en JSON-fil og ind i current shell environment
  • renew-keytab.sh - beregnet til at oprette/genskabe keytabs
  • update-dipex.sh - beregnet til at opdatere dette git-repo med ny kode, requirements etc

job-runner.sh

VIGTIGT: Det er nødvendigt at ændre gruppeejerskab, så det er alignet med den gruppe, der kører docker, Denne gruppe skal eje både hele os2mo-data-import-and-export og det directory hvor konfigurationsfiler ligger I praksis betyder det for os at vi ændrer ejerskabet på det dertil indrettede CRON-dicrectory i systembrugerens hjemmemappe.

Job runner scriptet er ment til at blive kaldt fra crontab på kundens maskiner Dets arbejde er:

  • at læse konfigurationen fra settings/settings.json, som er et symbolsk link til settings-filen for systemet.
  • at køre de prædefinerede dele af nattens cronjob forudsat at de er slået til i konfigurationen
  • at lave en backup af databasen og andre filer, der skal i spil for at få systemet tilbage til en veldefineret tilstand

Konfigurationen kan se således ud:

{
    "crontab.SVC_USER": "USER@KOMMUNE.NET",
    "crontab.SVC_KEYTAB": "/path/keytab-file",
    "crontab.CRON_BACKUP": "/path/backup-dir",
    "crontab.CRON_LOG_FILE": "/path/cron-log-file",
    "crontab.RUN_MOX_DB_CLEAR": false,
    "crontab.RUN_CHECK_AD_CONNECTIVITY": false,
    "crontab.RUN_BALLERUP_APOS": false,
    "crontab.RUN_BALLERUP_UDVALG": false,
    "crontab.RUN_QUERIES_BALLERUP": false,
    "crontab.RUN_SD_CHANGED_AT": false,
    "crontab.RUN_SD_FIX_DEPARTMENTS": false,
    "crontab.RUN_SD_DB_OVERVIEW": false,
    "crontab.RUN_AD_SYNC": false,
    "crontab.RUN_MOX_STS_ORGSYNC": false,
    "crontab.RUN_MOX_ROLLE": false,
    "crontab.RUN_CPR_UUID": false,
    "crontab.BACKUP_SAVE_DAYS": "60",
    "crontab.MOX_ROLLE_COMPOSE_YML":"",
    "crontab.SNAPSHOT_LORA":"/path/db-snapshot.sql"
}

En konfiguration som ovenstående kører ingen jobs, laver en backup i /path/backup-dir og sletter gamle backupper, når de er mere end 60 dage gamle. Det bruger AD-kontoen USER@KOMMUNE.NET når den skal connecte til AD og logger ind med /path/keytab-file, når det behøves og logger progress til /path/cron-log-file.

For at enable importen fra SD sættes crontab.RUN_SD_CHANGED_AT til true.

For at enable exporten til STS Organisation, sættes crontab.RUN_MOX_STS_ORGSYNC til true.

For at enable exporten to Rollekataloget, sættes crontab.RUN_MOX_ROLLE til true og crontab.MOX_ROLLE_COMPOSE_YML udfyldes med stien til den gældende docker-compose.yml file for Rollekatalogseksporten.

Ideen er at dette script kan kaldes fra cron, finder sin egen konfiguration, kører programmerne, hvorefter det laver en backup af /path/db-snapshot.sql og andre filer, der er nødvendige for at komme tilbage til en veldefineret tilstand.

Der kan være mere konfiguration nødvendig for de enkelte jobs - se disse for detaljer

Tilvejebringelse (og afslutning) af mountpoints styres af et script, cronhook.sh, som kaldes før og efter job-runner.sh. Dette script afvilker scripts i cronhook.pre.d og cronhook.post.d, hvis de er slået til i settings Sripts her afvikles i alfabetisk orden, og de bør hver især brokke sig over de settings de mangler

For at mounte opus-filer ind fra et windows share anvendes følgende settings:

  • cronhook.mount_opus_on: true/ false
  • cronhook.mount_opus_share - en windows unc-sti
  • cronhook.mount_opus_mountpoint - det mounpoint hvor sharet mountet
  • cronhook.mount_opus_username - brugernavn til sharet
  • cronhook.mount_opus_password - password til sharet

For at unmounte efter kørslen sættes denne setting, men lad være med det. Det besværliggør fejlfinding, hvis ikke der hele tiden er kontakt til filerne

  • cronhook.unmount_opus_on: true/false

Husk at mountpoints på windows ofte indeholder $-tegnet. Et sådan skal i settings escapes som \$

job-runner.sh er ikke et smart program. Dert er til gengæld simpelt.: Job-afviklingen foregår i 3 afdelinger: imports, exports og reports.

  • For alle jobs i imports og exports gælder at et fejlet job stopper afviklingen af de resterende jobs i den pågældfende afdeling
  • Hvis imports går galt, afvikles hverken exports eller reports
  • Hvis imports går godt forsøges både exports or reports afviklet

I ovenstående konfiguration kan man slå jobs til med alle de tilgængeglige crontab.RUN_*, som dækker over:

  • RUN_MOX_DB_CLEAR - Tøm OS2mo’s database
  • RUN_CHECK_AD_CONNECTIVITY - Check at der er di korrekte rettigheder i AD
  • RUN_SD_FIX_DEPARTMENTS - Kør SD-fix-departments
  • RUN_SD_CHANGED_AT - Kør SD-changed-at - deltaimport af ændringer fra SD
  • RUN_SD_UPDATE_PRIMARY - Kør Primærberegning af SD-employees
  • RUN_BALLERUP_APOS - Indlæs til OS2MO fra APOS (Ballerups version)
  • RUN_OPUS_DIFF_IMPORT - Kør Opus diff import - deltaimport af øndringer fra OPUS
  • RUN_AD_SYNC - Kør en AD-synkronisering
  • RUN_BALLERUP_APOS - total-indlæsning af APOS i Ballerup
  • RUN_BALLERUP_UDVALG - udvalgshierarkiet i Ballerups OS2MO
  • RUN_MOX_ROLLE - overførslen til rollekataloget
  • RUN_MOX_STS_ORGSYNC - Overførslen til STS Organisation
  • RUN_QUERIES_BALLERUP - Ballerups exports / forespørgsler
  • RUN_EXPORT_EMUS - Kør Eksport til EMUS
  • RUN_CPR_UUID - Lav en cachefile med CPR/UUID-sammenhænge - gøres typisk før en genindlæsning/restore
  • RUN_EXPORTS_TEST - Kør ingenting, men viser at job-runner har været i gang
  • RUN_SD_DB_OVERVIEW - Kør overbliksrapport over SD-indlæsningens progress (datoer)
  • RUN_OPUS_DB_OVERVIEW - Kør overbliksrapport over OPUS-indlæsningens progress (datoer)
  • RUN_AD_GROUP_INTO_MO - Importer en gruppe af eksterne ansatte som ikke findes i lønsystemet

Filer til backup er angivet i 3 afdelinger (bash-arrays):

  • BACK_UP_BEFORE_JOBS - fil lagres i backup inden kørslen af de enablede jobs afvikles
  • BACK_UP_AFTER_JOBS - fil lagres i backup efter at kørslen af de enablede jobs er afviklet
  • BACK_UP_AND_TRUNCATE - fil lagres i backup efter at kørslen af de enablede jobs er afviklet, hvorefter fil trunkeres til størrelse 0. Dette er praktisk til logfiler, som nu pakkes sammen med det datagrundlag, der skal til for at gentage kørslen.

Pakning af backup foregår i to afdelinger:

  • pre_backup - her pakkes alle filer i BACK_UP_BEFORE_JOBS sammen i en tidsstemplet tarfil
  • post_backup - her pakkes filerne i BACK_UP_AFTER_JOBS og BACK_UP_AND_TRUNCATE ned i tarfilen, som gzippes og filerne i BACK_UP_AND_TRUNCATE trunkeres.

Lagringen af backup foregår i servicebrugerens hjemmedirectory, se crontab.CRON_BACKUP i konfigurationseksemplet ovenfor.

Det kan, for eksempel under udfikling eller test, være nødvendigt at afvikle en kørsel ‘i hånden’ Den mulighed har man også med job-runner scriptet. Man giver simpelhen navnet på den indre funktion med i kaldet.

Herefter læses konfiguration på normal vis, men der tages nu ikke hensyn til om jobbet er slået til i konfigurationen eller ej, det køres

Følgende interne funktioner kan kaldes:

  • imports_test_ad_connectivity
  • imports_sd_fix_departments
  • imports_sd_changed_at
  • imports_opus_diff_import
  • imports_sd_update_primary
  • imports_ad_sync
  • imports_ballerup_apos
  • imports_ballerup_udvalg
  • exports_mox_rollekatalog
  • exports_mox_stsorgsync
  • exports_os2mo_phonebook
  • exports_cpr_uuid
  • exports_viborg_emus
  • reports_sd_db_overview
  • reports_opus_db_overview
  • exports_queries_ballerup
  • exports_test
  • imports
  • exports
  • reports

Vil man for eksempel afvikle mox_stsorgsync, anvender man kaldet:

tools/jon-runner.sh exports_mox_stsorgsync

Man kan source (. tools/job-runner.sh) for at få sat sit environment op. Dermed kan man få adgang til at anvende samme backup/restore funktionalitet, som anvendes af job-runner.sh / cron-restore.sh. Se tools/opus_import_all.sh for hvordan man angiver filer, der skal backes op måske trunkeres efterfølgende. Det er vigtigt at du bruger dit eget suffix - se her også eksemplet i tools/opus_import_all.sh

i settings findes mulighed for at logge til distribueret log. Det er værdien crontab.CRON_LOG_JSON_SINK, der bestemmer, hvor loggen skrives. Hvis den er slået til skrives der jsonlines til denne fil med status på både de store linier og de enkelte jobs. Hvis den ikke er slået til, gives der en warning i det almindelige logoutput.

I tillæg til denne fil pakker vi de jsonlinier, der vedrører nærværende kørsel af job-runner, ned i den backup-fil, som vedrører kørslen. Det sker ved at vi skriver til filen crontab.CRON_LOG_JSON, som trunkeres efter pakning til log og kørsel.

Magenta anvender tidsseriedatabasen Prometheus til at opsamle metrikker på udstrækningen af de jobs, der afvikles af job-runner. Konfigurationsværdien crontab.CRON_LOG_PROM_API styrer både hvorvidt denne funktionalitet er slåt til og hvor man kalder api’et, som typisk vil være igennem en såkaldt push-gateway på localhost.

clear_mox_tables.py

Dette anvendes typisk kun af cron-restore og der, hvor man hver nat genindlæser OS2mo fra APOS.

cron-restore.sh

Tømmer OS2MOS database og indlæser backup i mo og pakker den tilhørende run-db ud. Run-db er den lille sqlite-database, som fortæller SD-changed-at/opus_diff_import hvor langt den er kommet in indlæsningen.

Programmet køres som root på følgende måde:

bash tools/cron-restore.sh backupfil.tar.gz

backupfil.tar.gz er så en af de daterede filer, der ligger under sti-til-service-bruger/CRON/backup

Programmet er 17/3 2020 skrevet om til at håndtere os2mo under docker.

moxklas.sh

Anvendes under implementering til at oprette klasser i Lora. Nogle gange også efterfølgende. Scriptet er simpelt, men ikke så simpelt at kalde:

For at oprette en Email-addresse-klasse med en predefineret uuid under facetten employee_address_type udføres:

uuid=68d3d0ce-9fde-11ea-80b1-63a0ea904cea facet=employee_address_type bvn=test-moxklas titel=test-moxklas scope=EMAIL bash tools/moxklas.sh

Man kan provokere et dry-run ved at sætte en parameter efter hele linien

uuid=68d3d0ce-9fde-11ea-80b1-63a0ea904cea facet=employee_address_type bvn=test-moxklas titel=test-moxklas scope=EMAIL bash tools/moxklas.sh 42

Ovenstående sender et payload til lora, som opretter en klasse som ligner nedenstående

{
  "attributter": {
    "klasseegenskaber": [
      {
        "brugervendtnoegle": "test-moxklas",
        "titel": "test-moxklas",
        "omfang": "EMAIL",
        "virkning": {
          "from": "1930-01-01 12:02:32",
          "to": "infinity"
        }
      }
    ]
  },
  "tilstande": {
    "klassepubliceret": [
      {
        "publiceret": "Publiceret",
        "virkning": {
          "from": "1930-01-01 12:02:32",
          "to": "infinity"
        }
      }
    ]
  },
  "relationer": {
    "ansvarlig": [
      {
        "uuid": "8a2ae31b-422a-4374-b3a8-a2ed4ed23c63",
        "virkning": {
          "from": "1930-01-01 12:02:32",
          "to": "infinity"
        },
        "objekttype": "organisation"
      }
    ],
    "facet": [
      {
        "uuid": "332e8b38-68c3-4457-a5fb-3332216bb7a6",
        "virkning": {
          "from": "1930-01-01 12:02:32",
          "to": "infinity"
        }
      }
    ]
  }
}

opus_import_all.sh

Anvendes under initialindlæsning af opus filer til det mellemliggende trin, der er imellem den første komplette indlæsning og det tidspunkt, hvor man bare indlæser filen fra natten før. Programmet forsøger at indlæse alle opus-filer på en gang, og skulle det fejle markeres programmet efter et ekstra fejlet gennemløb og backup skal derefter indlæses.

Programmet kører som root med

bash tools/opus_import_all.sh

Opus_import_all.sh anvender intensivt settings/settings.json. Se under Opus-indlæsning i dokumentationen for at finde ud af hvilke settings, der skal være defineret for indlæsning fra Opus.

Programmet slutter af med at fortælle hvilken dato, der tilhører hvilken logfil, så man kan spole tilbage fra før fejlen opstod. ‘At spole tilbage’ gøres så med tools/cron-restore.sh

prefixed_settings.sh

prefixed_settings sources og anvender to environment-variable, med følgende defaults:

export SETTING_PREFIX=${SETTING_PREFIX:=crontab}
export CUSTOMER_SETTINGS=${CUSTOMER_SETTINGS:=/opt/settings/customer-settings.json}

Det omsætter værdier fra ovenstående konfigurationsfil, fjerner et prefix og eksporterer værdierne

Med øverststående konfigurationsfil ville der efter en sourcing af scriptet eksistere en nøgle SVC_USER i environment med værdien USER@KOMMUNE.NET

renew-keytab.sh

Dette interaktive program gør det muligt med lidt trial-and-error, når man skal have frembragt en brugbar keytab-fil.

update-dipex.sh

Dette program anvendes for at opdatere repositoriet og afhængigheder

inspect_config.py

compare settings file with kommune-anddeby.json and report what is missing

job-runner.d

Job-runner.d er konponenter, der loades af job-runneren Indtil nu loades funktionen, der afvikler jobs herigennem ligesom nyeste tilføjelse: tidsmålinger gør.

terminate_orgfunc.py

Et tool, som terminerer ALLE brugeres adresser og it-forbindelser. Det er jo ikke særligt smart at køre sådan et, for så skal man oprette dem allesammen igen. Det er imidlertid nødvendigt, hvis man er Viborg og tidligere har brugt Skole-AD eller man ændrer feltmapning