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...
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.
