page header photo

maart 2010 Archieven

Nabedrukt briefpapier


Geplaatst door gait op 2010-03-23 14:43:06 | Permanente link | Categorie: Tips and Tricks | Reacties: 0

Bij AT Computing zijn we overstapt van compleet voorbedrukt briefpapier naar papier met slechts het logo voorbedrukt. Daarvoor gebruiken we een OpenOffice.org-sjabloon met macro's: Voor een nieuw document kun je, uitgaande van zo'n sjabloon, kiezen uit drie briefhoofden: voorzien van de gegevens van een van de werkmaatschappijen ('oplossers' of 'opleiders') dan wel een algemene versie zonder bankrekeningnummers e.d.

Dat biedt geen soelaas voor bestaande documenten die voorheen op voorbedrukt briefpapier werden afgedrukt. En die zijn er: er wordt bijvoorbeeld gefactureerd vanuit een werkblad (spreadsheet). Daarvoor werd aanvankelijk eerst naar behoefte papier met briefhoofd afgedrukt waar in een tweede printgang de facturen op afgedrukt werden. Dat is omslachtig, alleen al omdat je er nog steeds een voorraad op na moet houden. Dat kan beter.

Filter

Om te beginnen wilde ik de vaste tekst van het briefhoofd electronisch samenvoegen met de variabele factuurtekst. Dat was niet moeilijk, hooguit wat omslachtig: beide componenten omzetten naar PDF, dan deze met de PDF-toolkit (pdftk) samenvoegen en weer terug naar PostScript vertalen. Dat trucje is zo gescript en daar hou je een postscript-naar-postscript filter aan over waarbij elke pagina voorzien is van het briefpapier.

Specifieke OO.o-printer

Het trucje is bruikaar bij een homebrew printer in OpenOffice.org. Als CUPS in gebruik is bij OpenOffice.org dan kun je met spadmin, het printerbeheerprogramma, normaal gesproken geen nieuwe printers maken. Maar je kunt nog wel zoals dat heet een 'PDF-conversieprogramma aansluiten'. Hierbij heb je zelf het hele traject van OO.o naar printer in de hand: Je stuurt dan het PostScript dat uit OO.o komt eerst door het filter waarna je de uitvoer naar de printer stuurt. Dan werkt het voor OO.o-gebruikers.

Implementatie in CUPS

Het mooiste zou zijn: de truc als optie bij het afdrukken beschikbaar maken. Dat doe je door het traject van een printjob in CUPS aan te passen. Een printtraject bestaat uit een pijplijn van filters die gekozen worden op basis van het MIME-types van de invoer en het MIME-type dat geschikt is voor een printer: Voor een overgang van het ene naar het andere MIME-type worden filters gebruikt. Voor zo'n filter ligt vast dat CUPS de opties als het vijfde argumenten doorgeeft. Mijn filter kijkt of het bekende opties aantreft (bijvoorbeeld atc-printedlogo') en doet dan het bijbehorende kunstje, anders filtert het ongewijzigd (/bin/cat).

Om het eigen printfilter in het filterpad van CUPS op te nemen heb ik een MIME-type toegevoegd aan de CUPS-configuratie door een nieuw types-bestand te maken volgens mime.types(1). In het nieuwe bestand /etc/cups/atc-letterhead.types staat

application/atc-letterhead

Vervolgens heb ik in het bestaande bestand /etc/cups/mime.convs een regel vervangen door twee nieuwe regels:

# invoertype            uitvoertype                     kosten filter
application/postscript  application/vnd.cups-postscript 66     pstops

is vervangen door

application/postscript     application/atc-letterhead      66 atc-letterhead2ps
application/atc-letterhead application/vnd.cups-postscript 66 pstops

Op die kosten kom ik later nog terug. Deze constructie zorgt ervoor dat mijn filter in het filtertraject opgenomen wordt. Het genoemde filter wordt gevonden als /etc/lib/cups/filter/atc-letterhead2ps en dat filter weet zelf waar de PDF-bestanden met briefhoofden staan.

De facturen worden rechtstreeks vanuit OO.o afgedrukt op de printer 'Oplossersfactuur inclusief logo op briefpapier'. Hierachter gaat het volgende commando schuil:

lp -dnetlp -o atc-oplossersfactuur

Het filter wordt altijd gebruikt. Het speelt doorgeefluik (cat), tenzij er een een optie voorbij komt die herkend wordt.

Printer instances

Ik heb meteen iets gemaakt waardoor je alle invoer kunt voorzien van een afgedrukt logo:

lp -dnetlp -oatc-printedlogo

Dat kan mooier als je in /etc/cups/lpoptions (of in je eigen ~/.cups/lpoptions) een zogenaamde printer-instance opvoert:

Dest netlp/printedlogo PageSize=A4 Duplex=DuplexNoTumble atc-printedlogo

Hierbij is netlp de printer en printedlogo de printer-instance. Let wel: lpoptions wordt slechts door CUPS-clients geraadpleegd.

Zoals je ziet kun je zo ook andere opties vastleggen, die instellingen krijg je kado als je de instance gebruikt:

lp -dnetlp/printedlogo

Helaas zijn printer instances binnen CUPS nog niet helemaal goed voorelkaar, want in de uitvoer van lptstat -t komen de instances voorbij alsof het zelfstandige print queues zijn. OO.o laat ze wel allemaal zien, maar ze werken allemaal gelijk omdat OO.o niet naar lpoptions kijkt. Daarvoor is hier in de vorm van een PDF-conversieprogramma een workaround gepresenteerd. Acrobat Reader geeft je wel een lijst met printernamen maar zet daar niet de instance-aanduiding achter ...

RPM

Ik heb van mijn oplossing een RPM gemaakt. Maar: ik had een bestaand bestand aangepast (mime.convs), dat bestand zit tevens in de CUPS-RPM, en als je een RPM met hetzelfde bestand wit installeren heb je een conflict. Dan kijk je nogmaals in de manpages mime.convs(1) en mime.types(1) en lees je het advies om geen bestaande files aan te passen doch lokale aanpassingen in lokale bestanden op te slaan. Ik had al een eigen .types-bestand gemaakt. Maar ja, hoe zet je dan dat filtertraject naar je hand?

Toen had ik een droom over de kosten die in de derde kolom van een .convs-bestand staan: hier staan de relatieve kosten van het filter en op basis daarvan kiest CUPS altijd de goedkoopste route. Dat was de crux: ik heb mijn alternatieve filterregels tegen lagere kosten in het nieuwe bestand /etc/cups/atc-letterhead.convs gezet:

application/postscript          application/atc-letterhead      22      atc-letterhead2ps
application/atc-letterhead      application/vnd.cups-postscript 22      pstops

Dit is de alternatieve manier om mijn filter in het filtertraject op te nemen.

Bingo!

BTRFS


Geplaatst door miekg op 2010-03-18 15:37:01 | Permanente link | Categorie: Infrastructuur | Reacties: 0

In het kader van redundantie gebruikt AT Computing altijd meerdere disks in haar servers. Als je zoiets met Linux dan opzet kom je al gauw uit op LVM en/of RAID. Nu heeft SUN een paar jaar geleden ZFS gepresenteerd. ZFS is zo geavanceerd dat alle toen bestaande file systemen meteen verouderd waren. Helaas is de licentie van ZFS dusdanig (CDDL) dat de ZFS code niet in de Linux kernel mag worden opgenomen. Gelukkig is Chris Mason (van Oracle — de nieuwe eigenaar van SUN), begonnen aan een file systeem dat nog beter dan ZFS moet worden en wel de juiste licentie (GPLv2) heeft om in Linux te worden opgenomen. Dit nieuwe file systeem heet: BTRFS. Je spreekt dat uit als 'the Better File System', maar het staat voor 'B-TRee FS'.

BTRFS:

  • heeft RAID ingebouwd (natuurlijk gestolen van ZFS;
  • verzorgt checksumming tussen verschillende devices; dus als een disk stuk aan het gaan is, zal BTRFS dat merken;
  • heeft snapshotting, een snapshot is een momentopname van een bestandssysteem; vergelijkbaar met snapshots in LVM (maar dan zonder het performance verlies van LVM);
  • heeft subvolumes, vergelijkbaar met logical volumes in LVM;
  • bevindt zich nog in een alpha/béta stadium.

Om eens te kijken wat BTRFS nu precies kan, hebben we even gespeeld met de nieuwe Ubuntu 10.04 Alpha-3, zodat we in ieder geval de nieuwste versie van BTRFS hebben.

Linux kan met grub2 nog niet booten van een BTRFS volume. Om dit euvel te verhelpen booten we nog gewoon van een RAID1 partitie met ext4. Er blijven uiteindelijk twee partities over voor BTRFS: /dev/sda4 en /dev/sdb4. Deze willen we inrichten als RAID0. BTRFS ondersteunt ook RAID1 en RAID10 en aan RAID5 wordt gewerkt. Voor het verdere verhaal maak de RAID versie niet uit.

Ingebruikname

We beginnen met het aanmaken van een BTRFS filesysteem op de twee partities: (naar de 'WARNING!' regels moet je nog even niet kijken)

mkfs.btrfs /dev/sda4 /dev/sdb4

WARNING! - Btrfs Btrfs v0.19 IS EXPERIMENTAL
WARNING! - see http://btrfs.wiki.kernel.org before using

adding device /dev/sdb4 id 2
fs created label (null) on /dev/sda4
nodesize 4096 leafsize 4096 sectorsize 4096 size 451.46GB
Btrfs Btrfs v0.19

En hier valt dus al gelijk op dat je (anders dan de meeste mkfs.* tools) twee (of nog meer) device namen kunt opgeven. Na deze vrij vlotte operatie kunnen we onze fonkelnieuwe ruimte gaan mounten:

# mount -t btrfs /dev/sda4 /mnt

We hadden hier ook /dev/sdb4 mogen gebruiken, BTRFS weet dat deze devices bij elkaar horen.

Nu kunnen we er files naar toe kopiëren en een beetje rondneuzen. Alles werkt zoals je verwacht dat het zou moeten werken. Er vallen geen bitjes op de grond.

Productie

Nu willen we natuurlijk het filesysteem gaan gebruiken vanaf het booten van de machine. Daartoe zetten we het in de fstab

/dev/sda4       /mnt            btrfs   defaults        0       0

En rebooten we. En toen was het stuk. Het blijkt dat BTRFS het filesysteem eerst even wil onderzoeken en het pas daarna gaat mounten (dit zal in latere versies worden verbeterd). Je moet daarvoor btrfsctl gebruiken:

# btrfsctl -A /dev/sda4
mount /mnt
mount: wrong fs type, bad option, bad superblock on /dev/sda4,
...
# btrfsctl -A /dev/sdb4
mount /mnt          # nu is het wel succesvol

Allebei de partities van ons BTRFS volume moeten zo onderhanden worden genomen. De standaard Linux boot scripts snappen dit nog niet en dus wordt het zo wel erg moeilijk om productie te gaan draaien met BTRFS.

Performance

We hebben we paar hele kleine testjes gedaan op de (oude) test hardware:

# dd if=/dev/zero bs=10240 count=102400 of=LARGE
102400+0 records in
102400+0 records out
1048576000 bytes (1.0 GB) copied, 6.69901 s, 157 MB/s

# dd if=/dev/zero bs=10240 count=102400 of=LARGE
102400+0 records in
102400+0 records out
1048576000 bytes (1.0 GB) copied, 6.46881 s, 162 MB/s

# time rm LARGE     # en weer weggooien
real    0m0.574s

Dat valt in de categorie bruikbaar. De insteek van de ontwikkelaars van BTRFS is: first make it work, then make it fast.

Subvolumes

Zoals gezegd zijn subvolumes vergelijkbaar met logical volumes van LVM. Je knipt eigenlijk een stuk ruimte uit je BTRFS volume en kunt dat apart gaan gebruiken. De grootte van een subvolume is net zo groot als het originele BTRFS volume waar die uit komt. Je kunt wel een soort quota zetten op een subvolume en zo de grootte beperken; dat hebben we hier niet getest.

Laten we een subvolume maken met de naam home, we gebruiken hiervoor weer btrfsctl:

 # btrfsctl -S home /mnt
 operation complete
 Btrfs Btrfs v0.19

Na deze operatie is er dus een subvolume home gemaakt dat net zo groot is als het originele BTRFS volume.

En nu gebruiken:

# mount -t btrfs -o subvol=home /dev/sda4 /mnt/home

Subvolumes zijn ook de basis voor snapshotting. Een snapshot is te maken door btrfsctl -s (kleine 's') te gebruiken. Ook die snapshots kun je mounten alsof het een normaal volume is.

Standaard features

Zaken die we ondertussen gewend zijn van andere file systemen, zoals bijveerbeeld online resizing worden gewoon ondersteund, zie voor de details btrfsctl(8) en de -r vlag.

Conclusie

Op een single disk kun je best al BTRFS uitproberen. Het schijnt dat Linus Torvalds al een half jaar geleden overgestapt is op BTRFS. Wil je de geavanceerde features uitproberen (meerdere disks, RAID, CRC-checking), dan kom je toch in een béta-wereld van zaken die wel werken, maar nog niet lekker in de rest van het systeem zitten ingebakken: een btrfsctl voordat je kunt mounten, subvolumes die niet meteen zichtbaar zijn, etc.

Ik kijk reikhalzend uit naar een 1.0 release van BTRFS, maar vooralsnog blijf ik op ext4 + RAIDN zitten. Of Oracle moet ZFS opnieuw uitbrengen, maar dan met een GPLv2 licentie (maar die kans is heel klein).

Kijk voor meer informatie op de Wikipedia pagina over BTRFS, of de weinig up-to-date wiki pagina van het project zelf.

Programmeren is een kunst...


Geplaatst door jc op 2010-03-01 14:19:32 | Permanente link | Categorie: Programmeren | Reacties: 0

Er was eens....

een bank met vele kantoren door het land. De medewerkers in de kantoren klaagden dat de nieuwe computers te traag waren. Dit speelde in de tijd dat electronisch bankieren, pinnen en geldautomaten nog een onbeduidende rol speelden. Klanten gingen naar de bank om geld op te nemen, en door de trage computers ontstonden er lange rijen. Daarom wilde de bank af van het contract met de leverancier.

Maar de leverancier stuurde een performance analyse en tuning expert om de oorzaak van de problemen te onderzoeken. Hij zag al snel dat één specifiek programma vrijwel alle CPU capaciteit opsoupeerde. Met een profiling tool kon hij inzoomen op het programma, en vond hij de uiteindelijke oorzaak. In de source code van het programma stond:

for (i=0; i<strlen(s); ++i) {
    if (... s[i] ...) ...
}

Die string s was gemiddeld vele duizenden karakters lang en iedere aanroep van strlen loopt langs alle karakters van de string op zoek naar de afsluitende null-byte. De string veranderde echter nooit in het lusje, dus hoefde de programmeur ook maar één keer de lengte vast te stellen. Daarmee zouden duizenden aanroepen van strlen bespaard kunnen worden, en dus miljoenen karaktervergelijkingen.

De code (door de bank zelf geschreven) werd snel aangepast, en de bankbedienden leefden nog lang en gelukkig:

n=strlen(s);
for (i=0; i<n; ++i) {
    if (... s[i] ...) ...
}

Had de programmeur niet beter zijn best moeten doen? Hij had toch moeten weten dat door de code zo op te schrijven als hij gedaan had, de code CPU-tijd nodig heeft die kwadratisch schaalt met de lengte van de string?

Iedere programmeur kent het motto: "first make it work, then make it work fast". Hiermee worden de valkuilen van micro-optimalisatie voorkomen. Maar het bovenstaande voorbeeld zou je bijna doen geloven dat de programmeur een Machiavelliaans motto aanhing "first make it work slowly."

Deze onnadenkendheid komt regelmatig voor. En het is niet alleen maar een kwestie van "je moet niet proberen het wiel opnieuw uit te vinden". Soms tikken onwetende programmeurs zonder na te denken "in het wilde weg" en hebben ze op die manier zomaar bubble sort "uitgevonden". En dan zijn ze er misschien nog trots op ook!

Naast het kiezen van het goede algoritme, moet je ook de goede datastructuur kiezen voor het soort probleem dat je aan het oplossen bent. De keuze voor een datastructuur kan erg veel invloed hebben. Als je bijvoorbeeld een gelinkte lijst gebruikt voor de opslag van miljoenen items waar je ook nog in wilt kunnen zoeken, heb je weer een suboptimale oplossing gevonden. Een gehashte datastructuur of een binaire boom laat je in dit geval de te zoeken data veel sneller vinden.

Programmeurs moeten dus het wiel niet proberen uit te vinden, en zoveel mogelijk gebruik maken van bestaande bibliotheken. Maar om problemen zoals die van de bank hierboven te vermijden, moeten de programmeurs wel wéten wat de eigenschappen van algoritmes en datastructuren zijn. Goed programmeerwerk begint dus bij een goede opleiding. Is het alleen maar de mooie opschmück in moderne tekstverwerkers die er voor zorgt dat ze net zo traag aanvoelen als old-school programma's zoals WordStar die in de jaren 80 van de vorige eeuw op 8-bits processor's draaiden in computers met 64KB geheugen?

Velen zeggen dat hergebruik is waar het om draait. Maar bovenal moeten programmeurs weten wanneer je wat op welke manier moet hergebruiken. Ze moeten dus kennis hebben van het probleemdomein, en van algoritmes en datastructuren.

Een goede programmeur weet ook wanneer een inferieur algoritme toch betere resultaten op zal leveren. Als je bijvoorbeeld zeker weet dat je nooit meer dan vijf items wilt sorteren (denk aan de vijf dobbelstenen in een Yahtzee spel), kun je misschien wel het beste kiezen voor bubble sort, omdat dat bubble sort gegeven de beperkte probleemgrootte het snelst en simpelst is.

Dus: leer van anderen (open source helpt!) en lees goede boeken (die je dan wel ook moet snappen). En mocht je de boekenserie bij uitstek goed lezen (Donald Knuths ``The art of computer programming'') zou je nog mazzel kunnen hebben en je onsterfelijk kunnen maken ook: Don Knuth betaalt een hexadecimale dollar ($2,56) voor iedere fout die je in het boek ontdekt.