Par un détour à Yoda

{ }

recherche

Aller au contenu | Aller au menu | Aller à la recherche

rsyncing two hard drives on two servers

The current situation: I have two servers at online.net. I have to transfer all the datas from one server to another.

Let's rsync all that.

Lire la suite

Symfony2 and Doctrine: Print sql queries in a Command

For debugging purposes, it's sometimes usefull to print the queries executed by a Symfony2 Command. Here is my tiny piece of code to do it.

protected function execute(InputInterface $input, OutputInterface $output)
    {
        // your code here
 
        // printing sql queries
        $logger = $this->getContainer()->get('monolog.logger.doctrine');
 
        foreach ($logger->getLogs() as $log)
        {
            $output->writeln(sprintf('Query: %s', $log['message']));
        }
    }

vmware 7.1.4 and linux-2.6.38

Since few kernels, vmware won't compile, or at least the vmnet driver won't compile.

  CC [M]  /tmp/vmware-root/modules/vmnet-only/driver.o
/tmp/vmware-root/modules/vmnet-only/driver.c: In function ‘VNetFileOpUnlockedIoctl’:
/tmp/vmware-root/modules/vmnet-only/driver.c:1137: error: implicit declaration of function ‘lock_kernel’
/tmp/vmware-root/modules/vmnet-only/driver.c:1139: error: implicit declaration of function ‘unlock_kernel’
make[4]: *** [/tmp/vmware-root/modules/vmnet-only/driver.o] Erreur 1
make[3]: *** [_module_/tmp/vmware-root/modules/vmnet-only] Erreur 2
make[2]: *** [sub-make] Erreur 2
make[1]: *** [all] Erreur 2
make[1]: quittant le répertoire « /usr/src/linux-headers-2.6.38-2-686-bigmem »
make: *** [vmnet.ko] Erreur 2
make: quittant le répertoire « /tmp/vmware-root/modules/vmnet-only »

Fortunately, there is a patch for this !

diff -u vmnet-only//driver.c /usr/lib/vmware/modules/source/vmnet-only//driver.c
--- vmnet-only//driver.c        2011-03-26 01:37:29.000000000 -0400
+++ /usr/lib/vmware/modules/source/vmnet-only//driver.c 2011-05-05 10:36:11.918545812 -0400
@@ -264,11 +264,11 @@
                            struct file * filp)  // IN:
 {
    int ret = -ENOTTY;
-   lock_kernel();
+   lock_flocks();
    if (filp && filp->f_op && filp->f_op->ioctl == VNetFileOpIoctl) {
       ret = VNetFileOpIoctl(filp->f_dentry->d_inode, filp, iocmd, ioarg);
    }
-   unlock_kernel();
+   unlock_flocks();
    return ret;
 }

@@ -1134,9 +1134,9 @@
    if (filp && filp->f_dentry) {
       inode = filp->f_dentry->d_inode;
    }
-   lock_kernel();
+   lock_flocks();
    err = VNetFileOpIoctl(inode, filp, iocmd, ioarg);
-   unlock_kernel();
+   unlock_flocks();
    return err;
 }
#endif

Patch found on http://communities.vmware.com/message/1749211#1749211.

Here is the archive to extract in /usr/lib/vmware/modules/source. bunzip2 the file, it's final name must be vmnet.tar (do not extract the data from the tar file)

[Symfony] Coupling sfDoctrineGuardPlugin and fail2ban

Recently I created a quite sensible application using symfony 1.4. As many people, I chose sfDoctrineGuardPlugin as authentication/password recovery/account managing system. But even with this piece of software, you are still vulnerable to bruteforce attacks.

I though of hacking sfDoctrineGuardPlugin's code to create some rules and attack detections. But those systems are quite tricky to create and calibrate. It must not be too aggressive nor too permissive. I wanted something that can bail out someone after $some tries in a $certain time. That implied that I would have to record every try with the current time, and calculate a ratio within which a user can login or not. I seemed simple, then it got too complicated.

Lire la suite

Configuration de Xen sur une machine dedibox

Le tableau

Mon exemple se compose d'une machine Dedibox Pro R210. Il s'agit d'une machine avec un octo-proc Xeon L3426 à 1.87GHz. Ne vous laissez pas avoir par « si peu » de GigaHertz, ces machines n'ont rien à voir avec des processeurs milieu de gamme type i5/i7. Elle comporte 16Go de ram DDR ECC. Cette machine est sous debian squeeze (stable actuelle).

Le réseau

Ma configuration réseau est ainsi :

  • IP: 88.190.11.12
  • Gateway: 88.190.11.1
  • DNS: 127.0.0.1, 88.191.254.60, 88.191.254.70

J'ai une adresse IP failover que j'ai assigné à mon serveur :

  • Adresse IP failover 1: 88.190.223.115
  • Adresse redirigée sur 88.190.11.12
  • Reverse : debsqueeze.yoda-bzh.net
  • Mac virtuelle Xen : 00:16:3e:00:00:40

La configuration réseau dépend de votre réseau. Par exemple, si votre ip est 88.190.23.55, la gateway doit donc être 88.190.23.1.

user@dom0$ sudo ifconfig
eth0      Link encap:Ethernet  HWaddr 00:26:b9:84:ff:ce  
          inet adr:88.190.11.12  Bcast:88.190.11.255  Masque:255.255.255.0
          adr inet6: fe80::226:b9ff:fe84:ffce/64 Scope:Lien
          UP BROADCAST RUNNING MULTICAST  MTU:1500  Metric:1
          RX packets:107463 errors:0 dropped:0 overruns:0 frame:0
          TX packets:90482 errors:0 dropped:0 overruns:0 carrier:0
          collisions:0 lg file transmission:1000 
          RX bytes:19045383 (18.1 MiB)  TX bytes:10436963 (9.9 MiB)
          Interruption:16 Mémoire:da000000-da012800 

lo        Link encap:Boucle locale  
          inet adr:127.0.0.1  Masque:255.0.0.0
          adr inet6: ::1/128 Scope:Hôte
          UP LOOPBACK RUNNING  MTU:16436  Metric:1
          RX packets:70 errors:0 dropped:0 overruns:0 frame:0
          TX packets:70 errors:0 dropped:0 overruns:0 carrier:0
          collisions:0 lg file transmission:0 
          RX bytes:6949 (6.7 KiB)  TX bytes:6949 (6.7 KiB)
user@dom0$ sudo route -n
Table de routage IP du noyau
Destination     Passerelle      Genmask         Indic Metric Ref    Use Iface
88.190.11.0     0.0.0.0         255.255.255.0   U     0      0        0 eth0
0.0.0.0         88.190.11.1     0.0.0.0         UG    0      0        0 eth0

Installation de xen4

Vous devez installer les paquets suivant : libxenstore3.0 linux-image-2.6.32-5-xen-amd64 linux-image-xen-amd64 xen-hypervisor-4.0-amd64 xen-tools xen-utils-4.0 xen-utils-common xenstore-utils xenwatch

Avec la commande suivante :

user@dom0$ sudo aptitude installlibxenstore3.0 linux-image-2.6.32-5-xen-amd64 linux-image-xen-amd64 xen-hypervisor-4.0-amd64 xen-tools xen-utils-4.0 xen-utils-common xenstore-utils xenwatch

Par défaut, Debian ne boot pas sur le noyau xen[1]. Il faut donc modifier la configuration de grub pour booter sur ce noyau

user@dom0$ cd /etc/grub.d/
user@dom0$ sudo mv -i 20_linux_xen 09_linux_xen
user@dom0$ sudo update-grub2

Vous devez ensuite redémarrer la machine pour qu'elle boot sur ce nouveau noyau. Normalement elle redémarrera sans heurt.

Une fois sur le nouveau noyau, un petit test pour voir si tout s'est bien passé :

user@dom0$ sudo xentop

Si vous voyez quelque chose du genre :

xentop - 10:52:07   Xen 4.0.1
1 domains: 1 running, 0 blocked, 0 paused, 0 crashed, 0 dying, 0 shutdown
Mem: 16767188k total, 15117648k used, 1649540k free    CPUs: 8 @ 1862MHz
      NAME  STATE   CPU(sec) CPU(%)     MEM(k) MEM(%)  MAXMEM(k) MAXMEM(%) VCPUS NETS NETTX(k) NETRX(k) VBDS   VBD_OO   VBD_RD   VBD_WR  
VBD_RSECT  VBD_WSECT SSID
  Domain-0 -----r      16821  400.7   14896180   88.8   no limit       n/a     8    0        0        0    0        0        0        0
        0          0    0



  Delay  Networks  vBds  Tmem  VCPUs  Repeat header  Sort order  Quit  

C'est que tout s'est bien passé. Si vous avez une erreur concernant sysfs, c'est que votre noyau n'est pas le bon.

Configuration de xend

Il est très fortement conseillé d'utiliser vif-route au lieu de vif-bridge sur le réseau dedibox. Nous allons donc modifier xend en conséquence.

user@dom0$ sudo vim /etc/xen/xend-config.sxp

Vers la ligne 160 il faut commenter ces lignes :

(network-script network-bridge)

Puis un peu plus bas, décommenter

#(network-script network-route)
#(vif-script vif-route)

et modifier en

(network-script 'network-route netdev=eth0')
(vif-script vif-route)

Il faut aussi modifier le script de route de xen :

user@dom0$ sudo echo  'echo 1 > /proc/sys/net/ipv4/conf/eth0/proxy_arp' >> /etc/xen/scripts/network-route
user@dom0$ sudo /etc/init.d/xend restart

Configuration de xen-tools

Le paquet xen-tools contient plusieurs executables[2] permettant de créer facilement des images xen de plusieurs distributions.

Nous allons d'abord configurer les script pour l'utilisation de xen-create-image

user@dom0$ cd /etc/xen-tools
user@dom0$ sudo vim xen-tools.conf

Vous trouverez vers la ligne 129 les tailles par défaut de vous images, ainsi que la composition matérielle (nombre de CPU, quantité de RAM, type de système de fichiers, etc.) Ma configuration est ainsi :

#
##
#  Disk and Sizing options.
##
#
size   = 15Gb      # Disk image size.
memory = 128Mb    # Memory size
swap   = 512Mb    # Swap size
# noswap = 1      # Don't use swap at all for the new system.
fs     = ext3     # use the EXT3 filesystem for the disk image.
dist   = `xt-guess-suite-and-mirror --suite` # Default distribution to install.
image  = sparse   # Specify sparse vs. full disk images.

Vers la ligne 160 vous aurez la configuration réseau par défaut :

gateway = 88.190.11.12
netmask = 255.255.255.0

J'ai ici mis la configuration réseau de mon Dom0, mais ces valeurs seront écrasées par les options que nous passerons à xen-create-image.

Vers la ligne 205 j'ai rajouté :

#
# Uncomment the following line if you wish not to generate a new root
# password for the new guest.
#
# genpass = 0
#
genpass = 0

Par défaut, xen vous crée un mot de pass root aléatoire et vous l'affiche à l'écran quand il a fini de créer l'image. Si vous êtes un peu rapide/pressé, vous arriverez à faire en sorte que ce mot de pass disparaisse avant que vous ayez pu le noter. Avec cette directive, xen vous demandera le mot de pass que vous voudrez utiliser.

Ligne 228, toujours pour les mots de passe :

#
# Uncomment the following line if you wish to interactively setup a
# new root password for images.
#
# passwd = 1
#
passwd = 1

J'ai laissé les autres directives par défaut.

Création d'une image

user@dom0$ sudo xen-create-image --pygrub --dist=squeeze --ip=88.190.223.115 --mac=00:16:3e:00:00:40 --netmask=255.255.255.255 --hostname=debsqueeze

General Information
--------------------
Hostname       :  debsqueeze
Distribution   :  squeeze
Mirror         :  http://ftp.fr.debian.org/debian/
Partitions     :  swap            512Mb (swap)
                  /               15Gb  (ext3)
Image type     :  sparse
Memory size    :  128Mb
Kernel path    :  /boot/vmlinuz-2.6.32-5-xen-amd64
Initrd path    :  /boot/initrd.img-2.6.32-5-xen-amd64

Networking Information
----------------------
IP Address 1   : 88.190.223.115 [MAC: 00:16:3e:00:00:40]
Netmask        : 255.255.255.255


Creating partition image: /var/lib/xen//domains/debsqueeze/swap.img
Done

Creating swap on /var/lib/xen//domains/debsqueeze/swap.img
Done

Creating partition image: /var/lib/xen//domains/debsqueeze/disk.img
Done

Creating ext3 filesystem on /var/lib/xen//domains/debsqueeze/disk.img
Done
Installation method: debootstrap
Done

Running hooks
Done

No role scripts were specified.  Skipping

Creating Xen configuration file
Done
Setting up root password
Enter new UNIX password: 
Retype new UNIX password: 
passwd: password updated successfully
All done


Logfile produced at:
	 /var/log/xen-tools/debsqueeze.log

Installation Summary
---------------------
Hostname        :  debsqueeze
Distribution    :  squeeze
IP-Address(es)  :  88.190.223.115 
RSA Fingerprint :  35:cf:00:03:54:74:60:4a:a5:ca:4f:5f:41:d1:6a:91
Root Password   :  N/A

Lancement de la VM

user@dom0$ sudo xm create -c /etc/xen/debsqueeze.conf

L'option -c est importante. Elle permet d'avoir une console sur la machine virtuelle qui démarre. Sans cette option, la machine est détachée, vous devez y accéder via SSH. Seulement avec la configuration actuelle, votre machine virtelle n'est pour l'instant pas accessible.


    pyGRUB  version 0.6
 ┌────────────────────────────────────────────────────────────────────────┐
 │ Debian GNU/Linux 6.0                                                   │
 │ Debian GNU/Linux 6.0 (Single-User)                                     │
 │                                                                        │
 │                                                                        │
 │                                                                        │
 │                                                                        │
 │                                                                        │
 │                                                                        │
 └────────────────────────────────────────────────────────────────────────┘
     Use the ^ and v keys to select which entry is highlighted.            
     Press enter to boot the selected OS, 'e' to edit the      
     commands before booting, 'a' to modify the kernel arguments
     before booting, or 'c' for a command line.                 




     Will boot selected entry in  1 seconds



Started domain debsqueeze (id=4)
                                [    0.000000] Initializing cgroup subsys cpuset
[    0.000000] Initializing cgroup subsys cpu
[    0.000000] Linux version 2.6.32-5-xen-amd64 (Debian 2.6.32-30) (ben@decadent.org.uk) (gcc version 4.3.5 (Debian 4.3.5-4) ) #1 SMP Wed Jan 12 05:46:49 UTC 2011
[    0.000000] Command line: root=/dev/xvda2 ro root=/dev/xvda2 ro 
[    0.000000] KERNEL supported cpus:
[    0.000000]   Intel GenuineIntel
[    0.000000]   AMD AuthenticAMD
[    0.000000]   Centaur CentaurHauls
[    0.000000] ACPI in unprivileged domain disabled
[    0.000000] released 0 pages of unused memory
[    0.000000] BIOS-provided physical RAM map:
[    0.000000]  Xen: 0000000000000000 - 00000000000a0000 (usable)
[    0.000000]  Xen: 00000000000a0000 - 0000000000100000 (reserved)
[    0.000000]  Xen: 0000000000100000 - 0000000008000000 (usable)
[    0.000000] DMI not present or invalid.
[    0.000000] last_pfn = 0x8000 max_arch_pfn = 0x400000000
[    0.000000] init_memory_mapping: 0000000000000000-0000000008000000
[    0.000000] RAMDISK: 016b8000 - 02fd7000
[    0.000000] No NUMA configuration found
[    0.000000] Faking a node at 0000000000000000-0000000008000000
[    0.000000] Bootmem setup node 0 0000000000000000-0000000008000000
[    0.000000]   NODE_DATA [000000000002a000 - 0000000000031fff]
[    0.000000]   bootmap [0000000000032000 -  0000000000032fff] pages 1
[    0.000000] (7 early reservations) ==> bootmem [0000000000 - 0008000000]
[    0.000000]   #0 [0000000000 - 0000001000]   BIOS data page ==> [0000000000 - 0000001000]
[    0.000000]   #1 [000301a000 - 0003037000]   XEN PAGETABLES ==> [000301a000 - 0003037000]
[    0.000000]   #2 [0000006000 - 0000008000]       TRAMPOLINE ==> [0000006000 - 0000008000]
[    0.000000]   #3 [0001000000 - 0001697994]    TEXT DATA BSS ==> [0001000000 - 0001697994]
[    0.000000]   #4 [00016b8000 - 0002fd7000]          RAMDISK ==> [00016b8000 - 0002fd7000]
[    0.000000]   #5 [0002fd7000 - 000301a000]   XEN START INFO ==> [0002fd7000 - 000301a000]
[    0.000000]   #6 [0000008000 - 000002a000]          PGTABLE ==> [0000008000 - 000002a000]
[    0.000000] Zone PFN ranges:
[    0.000000]   DMA      0x00000000 -> 0x00001000
[    0.000000]   DMA32    0x00001000 -> 0x00100000
[    0.000000]   Normal   0x00100000 -> 0x00100000
[    0.000000] Movable zone start PFN for each node
[    0.000000] early_node_map[2] active PFN ranges
[    0.000000]     0: 0x00000000 -> 0x000000a0
[    0.000000]     0: 0x00000100 -> 0x00008000
[    0.000000] SFI: Simple Firmware Interface v0.7 http://simplefirmware.org
[    0.000000] SMP: Allowing 1 CPUs, 0 hotplug CPUs
[    0.000000] No local APIC present
[    0.000000] APIC: disable apic facility
[    0.000000] PM: Registered nosave memory: 00000000000a0000 - 0000000000100000
[    0.000000] Allocating PCI resources starting at 8000000 (gap: 8000000:f8000000)
[    0.000000] Booting paravirtualized kernel on Xen
[    0.000000] Xen version: 4.0.1 (preserve-AD)
[    0.000000] NR_CPUS:512 nr_cpumask_bits:512 nr_cpu_ids:1 nr_node_ids:1
[    0.000000] PERCPU: Embedded 30 pages/cpu @ffff88000306d000 s90328 r8192 d24360 u122880
[    0.000000] pcpu-alloc: s90328 r8192 d24360 u122880 alloc=30*4096
[    0.000000] pcpu-alloc: [0] 0 
[    0.000000] Xen: using vcpu_info placement
[    0.000000] Built 1 zonelists in Node order, mobility grouping on.  Total pages: 32187
[    0.000000] Policy zone: DMA32
[    0.000000] Kernel command line: root=/dev/xvda2 ro root=/dev/xvda2 ro 
[    0.000000] PID hash table entries: 512 (order: 0, 4096 bytes)
[    0.000000] Initializing CPU#0
[    0.000000] Checking aperture...
[    0.000000] No AGP bridge found
[    0.000000] Memory: 95476k/131072k available (3147k kernel code, 384k absent, 35212k reserved, 1908k data, 600k init)
[    0.000000] SLUB: Genslabs=14, HWalign=64, Order=0-3, MinObjects=0, CPUs=1, Nodes=1
[    0.000000] Hierarchical RCU implementation.
[    0.000000] NR_IRQS:4352 nr_irqs:512
[    0.000000] Console: colour dummy device 80x25
[    0.000000] console [tty0] enabled
[    0.000000] console [hvc0] enabled
[    0.000000] installing Xen timer for CPU 0
[    0.000000] Detected 1862.031 MHz processor.
[    0.004000] Calibrating delay loop (skipped), value calculated using timer frequency.. 3724.06 BogoMIPS (lpj=7448124)
[    0.004000] Security Framework initialized
[    0.004000] SELinux:  Disabled at boot.
[    0.004000] Dentry cache hash table entries: 16384 (order: 5, 131072 bytes)
[    0.004000] Inode-cache hash table entries: 8192 (order: 4, 65536 bytes)
[    0.004000] Mount-cache hash table entries: 256
[    0.004000] Initializing cgroup subsys ns
[    0.004000] Initializing cgroup subsys cpuacct
[    0.004000] Initializing cgroup subsys devices
[    0.004000] Initializing cgroup subsys freezer
[    0.004000] Initializing cgroup subsys net_cls
[    0.004000] CPU: L1 I cache: 32K, L1 D cache: 32K
[    0.004000] CPU: L2 cache: 256K
[    0.004000] CPU: L3 cache: 8192K
[    0.004000] CPU 0/0x3 -> Node 0
[    0.004000] CPU: Unsupported number of siblings 16
[    0.004000] Performance Events: unsupported p6 CPU model 30 no PMU driver, software events only.
[    0.004000] SMP alternatives: switching to UP code
[    0.004000] Freeing SMP alternatives: 28k freed
[    0.004164] Brought up 1 CPUs
[    0.004273] devtmpfs: initialized
[    0.007968] Grant table initialized
[    0.007975] regulator: core version 0.5
[    0.008033] NET: Registered protocol family 16
[    0.008814] PCI: setting up Xen PCI frontend stub
[    0.009293] bio: create slab <bio-0> at 0
[    0.009364] ACPI: Interpreter disabled.
[    0.009399] xen_balloon: Initialising balloon driver with page order 0.
[    0.009448] vgaarb: loaded
[    0.009508] PCI: System does not support PCI
[    0.009513] PCI: System does not support PCI
[    0.009592] Switching to clocksource xen
[    0.010783] pnp: PnP ACPI: disabled
[    0.010984] NET: Registered protocol family 2
[    0.011053] IP route cache hash table entries: 1024 (order: 1, 8192 bytes)
[    0.011268] TCP established hash table entries: 4096 (order: 4, 65536 bytes)
[    0.011311] TCP bind hash table entries: 4096 (order: 4, 65536 bytes)
[    0.011338] TCP: Hash tables configured (established 4096 bind 4096)
[    0.011343] TCP reno registered
[    0.011395] NET: Registered protocol family 1
[    0.011439] Unpacking initramfs...
[    0.029811] Freeing initrd memory: 25724k freed
[    0.036328] platform rtc_cmos: registered platform RTC device (no PNP device found)
[    0.036528] audit: initializing netlink socket (disabled)
[    0.036542] type=2000 audit(1298716438.501:1): initialized
[    0.041443] HugeTLB registered 2 MB page size, pre-allocated 0 pages
[    0.042805] VFS: Disk quotas dquot_6.5.2
[    0.042858] Dquot-cache hash table entries: 512 (order 0, 4096 bytes)
[    0.042933] msgmni has been set to 236
[    0.043109] alg: No test for stdrng (krng)
[    0.043164] Block layer SCSI generic (bsg) driver version 0.4 loaded (major 253)
[    0.043171] io scheduler noop registered
[    0.043175] io scheduler anticipatory registered
[    0.043179] io scheduler deadline registered
[    0.043212] io scheduler cfq registered (default)
[    0.046544] registering netback
[    0.048173] Linux agpgart interface v0.103
[    0.048207] Serial: 8250/16550 driver, 4 ports, IRQ sharing enabled
[    0.048412] input: Macintosh mouse button emulation as /devices/virtual/input/input0
[    0.048463] PNP: No PS/2 controller found. Probing ports directly.
[    0.049283] i8042.c: No controller found.
[    0.049349] mice: PS/2 mouse device common for all mice
[    0.049576] rtc_cmos rtc_cmos: rtc core: registered rtc_cmos as rtc0
[    0.174162] cpuidle: using governor ladder
[    0.174171] cpuidle: using governor menu
[    0.174188] No iBFT detected.
[    0.174612] TCP cubic registered
[    0.174791] NET: Registered protocol family 10
[    0.175436] lo: Disabled Privacy Extensions
[    0.175812] Mobile IPv6
[    0.175821] NET: Registered protocol family 17
[    0.175973] registered taskstats version 1
[    0.175995] XENBUS: Device with no driver: device/vbd/51714
[    0.176004] XENBUS: Device with no driver: device/vbd/51713
[    0.176011] XENBUS: Device with no driver: device/vif/0
[    0.176018] XENBUS: Device with no driver: device/console/0
[    0.176043] /build/buildd-linux-2.6_2.6.32-30-amd64-d4MbNM/linux-2.6-2.6.32/debian/build/source_amd64_xen/drivers/rtc/hctosys.c: unable to open rtc device (rtc0)
[    0.176072] Initalizing network drop monitor service
[    0.176219] Freeing unused kernel memory: 600k freed
[    0.176389] Write protecting the kernel read-only data: 4328k
Loading, please wait...
[    0.228234] udev[46]: starting version 164
[    0.316800] Initialising Xen virtual ethernet driver.
[    0.330826] blkfront: xvda2: barriers enabled
[    0.332243] Setting capacity to 31457280
[    0.332257] xvda2: detected capacity change from 0 to 16106127360
[    0.332903] blkfront: xvda1: barriers enabled
[    0.334222] Setting capacity to 1048576
[    0.334234] xvda1: detected capacity change from 0 to 536870912
Begin: Loading essential drivers ... done.
Begin: Running /scripts/init-premount ... done.
Begin: Mounting root file system ... Begin: Running /scripts/local-top ... done.
Begin: Running /scripts/local-premount ... done.
[    0.597917] kjournald starting.  Commit interval 5 seconds
[    0.597938] EXT3-fs: mounted filesystem with ordered data mode.
Begin: Running /scripts/local-bottom ... done.
done.
Begin: Running /scripts/init-bottom ... done.
INIT: version 2.88 booting
Using makefile-style concurrent boot in runlevel S.
Starting the hotplug events dispatcher: udevd[    0.762185] udev[147]: starting version 164
.
Synthesizing the initial hotplug events...done.
Waiting for /dev to be fully populated...[    0.871458] input: PC Speaker as /devices/platform/pcspkr/input/input1
[    0.973613] Error: Driver 'pcspkr' is already registered, aborting...
done.
Activating swap...[    1.095101] Adding 524280k swap on /dev/xvda1.  Priority:-1 extents:1 across:524280k SS
done.
Checking root file system...fsck from util-linux-ng 2.17.2
/dev/xvda2: clean, 13584/983040 files, 195590/3932160 blocks
done.
[    1.133306] EXT3 FS on xvda2, internal journal
Loading kernel modules...done.
Cleaning up ifupdown....
Setting up networking....
Activating lvm and md swap...done.
Checking file systems...fsck from util-linux-ng 2.17.2
done.
Mounting local filesystems...done.
Activating swapfile swap...done.
Cleaning up temporary files....
Setting kernel variables ...done.
Configuring network interfaces...SIOCADDRT: No such process
Failed to bring up eth0.
done.
Cleaning up temporary files....
INIT: Entering runlevel: 2
Using makefile-style concurrent boot in runlevel 2.
Starting enhanced syslogd: rsyslogd.
Starting OpenBSD Secure Shell server: sshd.
Starting periodic command scheduler: cron.

Debian GNU/Linux 6.0 debsqueeze hvc0

debsqueeze login:

Pour voir si votre machine virtuelle tourne, utilisez xentop :

user@dom0$ sudo xentop
xentop - 11:36:07   Xen 4.0.1
2 domains: 1 running, 1 blocked, 0 paused, 0 crashed, 0 dying, 0 shutdown
Mem: 16767188k total, 15245088k used, 1522100k free    CPUs: 8 @ 1862MHz
      NAME  STATE   CPU(sec) CPU(%)     MEM(k) MEM(%)  MAXMEM(k) MAXMEM(%) VCPUS NETS NETTX(k) NETRX(k) VBDS   VBD_OO   VBD_RD   VBD_WR  
VBD_RSECT  VBD_WSECT SSID
debsqueeze --b---          1    0.0     127232    0.8     131072       0.8     1    1        0        0    2        0      978       79
    28146       1000    0
  Domain-0 -----r      27539  400.7   14896180   88.8   no limit       n/a     8    0        0        0    0        0        0        0
        0          0    0


  Delay  Networks  vBds  Tmem  VCPUs  Repeat header  Sort order  Quit  

On peut voir que la machine debsqueeze tourne :)

Configuration réseau de la vm

Pour l'instant la configuration de la vm ressemble à ça :

root@debsqueeze:~# ifconfig
eth0      Link encap:Ethernet  HWaddr 00:16:3e:00:00:40  
          inet addr:88.190.223.115  Bcast:88.255.255.255  Mask:255.255.255.255
          inet6 addr: fe80::216:3eff:fe00:40/64 Scope:Link
          UP BROADCAST RUNNING MULTICAST  MTU:1500  Metric:1
          RX packets:4 errors:0 dropped:0 overruns:0 frame:0
          TX packets:6 errors:0 dropped:0 overruns:0 carrier:0
          collisions:0 txqueuelen:1000 
          RX bytes:244 (244.0 B)  TX bytes:468 (468.0 B)
          Interrupt:246 

lo        Link encap:Local Loopback  
          inet addr:127.0.0.1  Mask:255.0.0.0
          inet6 addr: ::1/128 Scope:Host
          UP LOOPBACK RUNNING  MTU:16436  Metric:1
          RX packets:8 errors:0 dropped:0 overruns:0 frame:0
          TX packets:8 errors:0 dropped:0 overruns:0 carrier:0
          collisions:0 txqueuelen:0 
          RX bytes:536 (536.0 B)  TX bytes:536 (536.0 B)

root@debsqueeze:~# route -n
Kernel IP routing table
Destination     Gateway         Genmask         Flags Metric Ref    Use Iface

Comme je le disais, par défaut, votre VM n'a pas encore accès au net. Pour ceci, vous allez devoir ajouter la gateway avec une certaine configuration

root@debsqueeze:~# route add 88.190.11.1/32 dev eth0
root@debsqueeze:~# route add default gw 88.190.11.1

Nous pouvons maintenant vérifier que nous avons le net à partir de la VM :

root@debsqueeze:~# ping -c 3 free.fr
PING free.fr (212.27.48.10) 56(84) bytes of data.
64 bytes from www.free.fr (212.27.48.10): icmp_req=1 ttl=122 time=0.868 ms
64 bytes from www.free.fr (212.27.48.10): icmp_req=2 ttl=122 time=0.895 ms
64 bytes from www.free.fr (212.27.48.10): icmp_req=3 ttl=122 time=0.948 ms

--- free.fr ping statistics ---
3 packets transmitted, 3 received, 0% packet loss, time 2004ms
rtt min/avg/max/mdev = 0.868/0.903/0.948/0.048 ms

Et depuis l'exterieur vers la vm :

moi@autremachine$ ssh root@88.190.223.115
root@88.190.223.115's password: 
Linux debsqueeze 2.6.32-5-xen-amd64 #1 SMP Wed Jan 12 05:46:49 UTC 2011 x86_64

The programs included with the Debian GNU/Linux system are free software;
the exact distribution terms for each program are described in the
individual files in /usr/share/doc/*/copyright.

Debian GNU/Linux comes with ABSOLUTELY NO WARRANTY, to the extent
permitted by applicable law.
Last login: Sat Feb 26 11:44:46 2011
root@debsqueeze:~# 

Pour mémoriser cette configuration, nous allons modifier le fichier /etc/network/interfaces (j'ai installé vim entre temps) :

root@debsqueeze:~# vim /etc/network/interfaces 
# This file describes the network interfaces available on your system
# and how to activate them. For more information, see interfaces(5).

# The loopback network interface
auto lo
iface lo inet loopback

# The primary network interface
auto eth0
iface eth0 inet static
        address 88.190.223.115
        netmask 255.255.255.255
        # les deux lignes à rajouter :
        post-up route add -host 88.190.11.1/32 dev eth0
        post-up route add default gw 88.190.11.1

 # post-up  ethtool -K eth0 tx off

#
# The commented out line above will disable TCP checksumming which
# might resolve problems for some users.  It is disabled by default
#

Erreurs

  • Si vous avez un peth0 qui est actif dans votre ifconfig, redémarrer votre machine. Cette interface doit venir de l'installation de xen en bridge. Faire un ifconfig peth0 down va tout simplement vous couper l'accès au réseau.

Sources :

Notes

[1] Sources: Bug 606719 chez Debian, Le wiki debian sur xen

[2] xt-customize-image, xt-create-xen-config, xen-list-images, xen-delete-image, xt-install-image, xen-create-nfs, xen-create-image, xt-guess-suite-and-mirror, xen-update-image

Symfony - Doctrine: Use a fully UTF8 database

First, edit your config/databases.yml and add :

[yml]

all:
  doctrine:
    class: sfDoctrineDatabase
    param:
      dsn:      mysql:socket=/var/run/mysqld/mysqld.sock;dbname=mydb
      username: mydb
      password: mypassword
      encoding: utf8
      attributes:
        default_table_collate: utf8_unicode_ci
        default_table_charset: utf8

Then open your config/doctrine/schema.yml and add at the top of your file :

|yml]
---
detect_relations: true
options:
  collation: utf8_unicode_ci
  charset: utf8
  type: InnoDB

Of course, if you want to stick to myisam, replace InnoDB by MyIsam.

Befware, some plugins provides their own schema.yml. You may have to modify it, or to inherite it and specify that they're in utf8 (to come later)

Symfony 1.2, 1.3 and 1.4

Doctrine: Howto bypass the SoftDelete behaviour

For one of my projects I have to check if a record already exists in my database, deleted or not. The preDqlSelect looks like this :

/**
     * Implement preDqlSelect() hook and add the deleted flag to all queries for which this model 
     * is being used in.
     *
     * @param Doctrine_Event $event 
     * @return void
     */
    public function preDqlSelect(Doctrine_Event $event)
    {
        $params = $event->getParams();
        $field = $params['alias'] . '.' . $this->_options['name'];
        $query = $event->getQuery();
        if ( ! $query->contains($field)) {
            $query->addWhere(
                $field . ' = ' . $query->getConnection()->convertBooleans(false) . ' OR ' . $field . ' IS NULL'
            );
        }
    }

The method is adding the statement only if !$query->contains($field), and contains is just doing an stripos on the DQL. At this time, the DQL is the part between the FROM to the end. It doesn't include the SELECT part. So to bypass the SoftDelete behaviour, the field deleted have to be in the DQL. A simple solution is to add a dummy statement like this :

$results = Doctrine_Query::create()
    ->from('Table t')
    ->where('t.deleted = t.deleted')
    ->execute()
    ;

Thanks to vjousse for the tip

Notify: New release of the aMSN plugin

A new release of the Notify plugin for aMSN is now available.

You can download it on http://www.amsn-project.net/plugins....

Changelog:

  • Fix UTF8 detection, thanks to bardo
  • Version 1.5

PHP bench: isset vs array_key_exists

I tested the rapidity of isset vs array_key_exists. I always thougt that array_key_exists would be the winner ...

Here is my script :

┌─(yoda@ev5)(14:25:29)
└─(~/var/www/test)-> cat isset_array-key-exists.php 
#!/usr/bin/php
<?php
 
define('N', "\n");
 
error_reporting(E_ALL | E_NOTICE);
 
$nbLoop = 10000;
 
$testArray = array('foo' => 'bar', 'bar' => 'quux');
 
$time = array();
 
$desc = array(
    0 => 'array_key_exists on existant key',
    1 => 'array_key_exists on non existant key',
    2 => 'isset on existant key',
    3 => 'isset on non existant key',
);
 
 
/**
 * begin with array_key_exists, existant key
 */
$time[0]['start'] = microtime(true);
 
for($i = 0; $i < $nbLoop; $i++) {
    array_key_exists('foo', $testArray);
}
 
$time[0]['stop'] = microtime(true);
 
 
/**
 * begin with array_key_exists, non existant key
 */
$time[1]['start'] = microtime(true);
 
for($i = 0; $i < $nbLoop; $i++) {
    array_key_exists('foz', $testArray);
}
 
$time[1]['stop'] = microtime(true);
 
 
 
/**
 * begin with isset, existant key
 */
$time[2]['start'] = microtime(true);
 
for($i = 0; $i < $nbLoop; $i++) {
    isset($testArray['foo']);
}
 
$time[2]['stop'] = microtime(true);
 
 
/**
 * begin with isset, non existant key
 */
$time[3]['start'] = microtime(true);
 
for($i = 0; $i < $nbLoop; $i++) {
    isset($testArray['foz']);
}
 
$time[3]['stop'] = microtime(true);
 
 
$differences = array();
echo 'results: '.N;
foreach($time as $k => $t) {
    echo 'test '.$k.' "'.sprintf('%40s', $desc[$k]).'" : '.($difference[$k] = $t['stop'] - $t['start']).N;
}

And here is the results :

┌─(yoda@ev5)(14:25:32)
└─(~/var/www/test)-> php isset_array-key-exists.php 
results: 
test 0 "        array_key_exists on existant key" : 0.00795984268188
test 1 "    array_key_exists on non existant key" : 0.00665903091431
test 2 "                   isset on existant key" : 0.00737690925598
test 3 "               isset on non existant key" : 0.00131511688232

And the winner is : isset !

PHP bench: count + for vs foreach vs while

I tested different loops. Here is my test script :

┌─(yoda@ev5)(14:32:05)
└─(~/var/www/test)-> cat for_foreach_while.php 
#!/usr/bin/php
<?php
 
define('N', "\n");
 
error_reporting(E_ALL | E_NOTICE);
 
$nbLoop = 10000;
 
$testArray = array_fill(0, 500, 'toto');
 
$time = array();
 
$desc = array(
	'iterating an array with a count + for',
	'iterating an array with a foreach',
	'iterating an array with a while',
);
 
 
/**
 * iterating with for
 */
echo 'Starting test 1'.N;
$time[0]['start'] = microtime(true);
 
for($i = 0; $i < $nbLoop; $i++) {
	$nb = count($testArray);
	for($j = ($nb - 1); $j > 0; $j--) {
	}
}
 
$time[0]['stop'] = microtime(true);
echo 'Test 1 ended'.N;
 
/**
 * iterating with foreach
 */
echo 'Starting test 2'.N;
$time[1]['start'] = microtime(true);
 
for($i = 0; $i < $nbLoop; $i++) {
	foreach($testArray as $k => $v) {
	}
}
 
$time[1]['stop'] = microtime(true);
echo 'Test 2 stopped'.N;
 
 
/**
 * iterating with while(list() = each())
 */
echo 'Starting test 3'.N;
$time[2]['start'] = microtime(true);
 
for($i = 0; $i < $nbLoop; $i++) {
	while(list($k, $v) = each($testArray)) {
	}
	reset($testArray);
}
 
$time[2]['stop'] = microtime(true);
echo 'Test 3 ended'.N;
 
 
$differences = array();
echo 'results: '.N;
foreach($time as $k => $t) {
    echo 'echo test '.$k.' "'.sprintf('%40s', $desc[$k]).'" : '.($difference[$k] = $t['stop'] - $t['start']).N;
}

And the results are :

┌─(yoda@ev5)(14:32:47)
└─(~/var/www/test)-> php for_foreach_while.php 
Starting test 1
Test 1 ended
Starting test 2
Test 2 stopped
Starting test 3
Test 3 ended
results: 
echo test 0 "   iterating an array with a count + for" : 0.489103078842
echo test 1 "       iterating an array with a foreach" : 0.750061988831
echo test 2 "         iterating an array with a while" : 4.91836500168

The winner is count+for, followed by foreach, and while is far behind. I suppose it's because of the reset.

PHP: 5.2.12 (debian) in CLI

Special thanks to Folliked for remembering me that while still exists ;)

Symfony: Validate fields depending of another field (update 2010-02-23)

It's possible, when validating a form, to check if a field is correct dependently of an other field's value.

First, in your configure method, after the declaration of your validators, add :

$this->validatorSchema->setPostValidator(
    new sfValidatorCallback(
        array('callback' => array($this, 'checkRequiredFields'))
    )
);

And create a new method in your form class !

public function checkRequiredFields($validator, $values) {
    $errors = array();
    /**
     * checking if mandatory_field == 3, then other_field must be filled
     */
    if(3 == $values['mandatory_field'] && null == $values['other_field']) {
        $error = new sfValidatorError($validator, 'if you choose the option 3 for mandatory_field, you need to fill the other_field');
        $errors['other_field'] = $error;
        //throw new sfValidatorErrorSchema($validator, array('other_field' => $error));
    } else {
        // nothing to check
    }
 
 
    /**
     * checking if foo is selected, then bar must be filled
     */
    if(1 == $values['foo'] && null == $values['bar']) {
        $error = new sfValidatorError($validator, 'if you check foo, you need to fill bar');
        $errors['bar'] = $error;
    } else {
        // nothing to check
    }
 
    /**
     * more depency checking here
     */
 
    /**
     * if there is errors
     */
    if(count($errors)) {
        throw new sfValidatorErrorSchema($validator, $errors);
    }
    /**
     * dont forget to return values !
     */
    return $values;
}

2010-02-23 Updated: All errors are now returned once. 2010-02-23 Updated: Deleted the commented throw, added another check.

Symfony: change the default type of your primary keys

Hey, yes it's possible to change the default type of your primary keys with a simple configuration

Open your config/databases.yml. It should looks like this :

 [yml]
# You can find more information about this file on the symfony website:
# http://www.symfony-project.org/reference/1_4/en/07-Databases

all:
  doctrine:
    class: sfDoctrineDatabase
    param:
      dsn:      mysql:host=localhost;dbname=.
      username: root
      password:

But Doctrine lets you customize some things. Now the file looks like :

 [yml]
# You can find more information about this file on the symfony website:
# http://www.symfony-project.org/reference/1_4/en/07-Databases

all:
  doctrine:
    class: sfDoctrineDatabase
    param:
      dsn:      mysql:host=localhost;dbname=.
      username: root
      password:
      attributes:
        default_identifier_options:
          type: integer
          length: 4
          unsigned: true

Nowyou can change the parameter, lets say an integer, and length: 5.

It's event possible to add any configuration that will be added automatically to your model.

Note: It's not possible to set autoincrement: false. True is forced by doctrine. Note: This behavior is true when no primary column is found in your schema.

Thanks to PhilG (#symfony-fr @ Freenode) for testing. Tested with symfony 1.4.1

Symfony, tasks and return values

In symfony, it's possible to return a value in your execute() method.

Let's create a test task :

┌─(yoda@box)(09:46:59)
└─(~/var/www/test)-> ./symfony generate:task test
>> task      Creating "/home/yoda/var/www/test.../testTask.class.php" task file

Adding a return value at the end of the method. :

<?php
 
class testTask extends sfBaseTask
{
  protected function configure()
  {
    // // add your own arguments here
    // $this->addArguments(array(
    //   new sfCommandArgument('my_arg', sfCommandArgument::REQUIRED, 'My argument'),
    // ));
 
    $this->addOptions(array(
      new sfCommandOption('application', null, sfCommandOption::PARAMETER_REQUIRED, 'The application name'),
      new sfCommandOption('env', null, sfCommandOption::PARAMETER_REQUIRED, 'The environment', 'dev'),
      new sfCommandOption('connection', null, sfCommandOption::PARAMETER_REQUIRED, 'The connection name', 'doctrine'),
      // add your own options here
    ));
 
    $this->namespace        = '';
    $this->name             = 'test';
    $this->briefDescription = '';
    $this->detailedDescription = <<<EOF
The [test|INFO] task does things.
Call it with:
 
  [php symfony test|INFO]|>
EOF;
  }
 
  protected function execute($arguments = array(), $options = array())
  {
    // initialize the database connection
    $databaseManager = new sfDatabaseManager($this->configuration);
    $connection = $databaseManager->getDatabase($options['connection'] ? $options['connection'] : null)->getConnection();
 
    // add your code here
    return 42;
  }
}

And now, execute the task :

┌─(yoda@box)(09:47:32)
└─(~/var/www/test)-> ./symfony test
┌─(yoda@box)(09:47:40)
└─(~/var/www/test)-> echo $?
42

The return value is correctly returned. Note that without return value, the task simply returns 0.

In the case that an exception occurs, let's see what is returned. I added a dummy exception in the execute method :

<?php
 
class testTask extends sfBaseTask
{
  protected function configure()
  {
    // // add your own arguments here
    // $this->addArguments(array(
    //   new sfCommandArgument('my_arg', sfCommandArgument::REQUIRED, 'My argument'),
    // ));
 
    $this->addOptions(array(
      new sfCommandOption('application', null, sfCommandOption::PARAMETER_REQUIRED, 'The application name'),
      new sfCommandOption('env', null, sfCommandOption::PARAMETER_REQUIRED, 'The environment', 'dev'),
      new sfCommandOption('connection', null, sfCommandOption::PARAMETER_REQUIRED, 'The connection name', 'doctrine'),
      // add your own options here
    ));
 
    $this->namespace        = '';
    $this->name             = 'test';
    $this->briefDescription = '';
    $this->detailedDescription = <<<EOF
The [test|INFO] task does things.
Call it with:
 
  [php symfony test|INFO]|>
EOF;
  }
 
  protected function execute($arguments = array(), $options = array())
  {
    // initialize the database connection
    $databaseManager = new sfDatabaseManager($this->configuration);
    $connection = $databaseManager->getDatabase($options['connection'] ? $options['connection'] : null)->getConnection();
 
    // add your code here
    throw new Exception('foo');
    return 42;
  }
}
┌─(yoda@box)(09:51:37)
└─(~/var/www/test)-> ./symfony test

               
  foo         
               

┌─(yoda@box)(09:51:43)
└─(~/var/www/test)-> echo $?
1

The task returns 1 in case of an exception.

[Symfony] Overwrite a value in a form

It happen sometimes that you have to set/override a value within a form but without using a hidden field (that can be overridden)

Here is a short solution to set the user id in the object we are going to save.

Let's say that we have an object PersonnalData linked to the sfGuardUserProfile

In your PersonnalDataForm.class.php :

class PersonnalDataForm extends BasePersonnalDataForm {
 
    public function configure() {
    }
 
    //public function processValues($values = null) {  // only in 1.2
    public function processValues($values) { // 1.3 - 1.4
        $values['user_id'] = $this->getOption('user_id');
        return parent::processValues($values);
    }
 
 
}

$this->getOption('user_id') is a mechanism to pass variables from outside the form to it. To set it, you must use the second argument of the form's constructor.

In your action :

public function executeMyaction(sfWebRequest $request) {
        //                                             setting an array containg the variable we want to use in our form
        $this->form = new PersonnalDataForm(null, array('user_id' => $this->getUser()->getProfile()->getId() ));
 
        if($request->isMethod('post')) {
            $this->form->bind($request->getParameter($this->form->getName()));
            if($this->form->isValid()) {
                $this->form->save();
            } else {
                //echo 'not valid';
            }
        }
    }

[Symfony] Load an sql dump within a task

I recently had a problem when loading fixtures in my symfony project. I was loading cities, about 37 000 of them, and the doctrine:data-load was extremly slow (about 5 minutes). I finally found a solution to load an sql dump wihtin a task, using the doctrine/default connection.

With this solution, I reduced the time from minutes to 2 secondes.

<?php
 
class loadALotOfDataTask extends sfBaseTask
{
  protected function configure()
  {
    // // add your own arguments here
    // $this->addArguments(array(
    //   new sfCommandArgument('my_arg', sfCommandArgument::REQUIRED, 'My argument'),
    // ));
 
    $this->addOptions(array(
      new sfCommandOption('application', null, sfCommandOption::PARAMETER_REQUIRED, 'The application name', 'frontend'),
      new sfCommandOption('env', null, sfCommandOption::PARAMETER_REQUIRED, 'The environment', 'dev'),
      new sfCommandOption('connection', null, sfCommandOption::PARAMETER_REQUIRED, 'The connection name', 'doctrine'),
      // add your own options here
    ));
 
    $this->namespace        = '';
    $this->name             = 'loadALotOfData';
    $this->briefDescription = '';
    $this->detailedDescription = <<<EOF
The [loadALotOfData|INFO] task does things.
Call it with:
 
  [php symfony loadALotOfData|INFO]|>
EOF;
  }
 
  protected function execute($arguments = array(), $options = array())
  {
    // initialize the database connection
    $databaseManager = new sfDatabaseManager($this->configuration);
    $connection = $databaseManager->getDatabase($options['connection'] ? $options['connection'] : null)->getConnection();
 
    // add your code here
    $file = '../data/dump.sql';
    if(!file_exists($file)) {
        $this->logSection('File', sprintf('File %s does not exists', $file));
        return;
    }
    // loading the file in the memory. In my case, the file was 
    $data = file_get_contents($file);
    // passing queries to the orm
    $statement = $connection->prepare($data);
    // executing
    $statement->execute();
  }
}

[PHP] Pensez à STDClass

Il arrive souvent qu'on ai besoin de retourner plusieurs informations distinctes dans une fonction ou une méthode. La plupart du temps, il suffit de retourner un tableau indexé de manière intelligente.

class CSS {
    public function getDeclaration($pDeclaration) {
        list($key, $value) = explode(':', $pDeclaration);
        $key = trim($key);
        $value = trim($value, ' ;');
        return array('key' => $key, 'value' => $value);
    }
};
$ret = CSS::getDeclaration('background-color: #FFFFFF;');
echo 'Clé: '.$ret['key'];
echo 'Valeur: '.$ret['valeur'];

Mais quand on utilise des frameworks tel que symfony où tout (ou presque) est objet, il devient difficile de chainer les éléments.

STDClass[1] vient donc à notre secours.

class CSS {
    public function getDeclaration($pDeclaration) {
        list($key, $value) = explode(':', $pDeclaration);
        $key = trim($key);
        $value = trim($value, ' ;');
        $r = new STDClass;
        $r->key = $key;
        $r->value = $value;
        return $r;
    }
};

Il devient maintenant possible de faire une déclaration du genre :

echo 'Clé: '.CSS::getDeclaration('background-color: #FFFFFF;')->key;
echo 'Valeur: '.CSS::getDeclaration('text-decoration: underline;')->value;

Update :

Petite mise à jour, j'avais fait une petite typo dans le premier bout de code, au niveau des echo-s. Merci Neolitec.

Notes

[1] http://php.net/stdclass

Asus 1005HA-H and Debian

I recently recevied my new Asus eee1005HA-H bough on Materiel.net. Everything seemed fine as there was a Debian project supporting the eee-s. But unfortunately the current installer couldn't support networking devices.

Lire la suite

My first plugin for symfony

Hey, I just uploaded my first plugin for symfony !

This plugin is a simple wrapper for a jQuery Carousel found at http://sorgalla.com/projects/jcarousel/

Display lastest dotclear posts in symfony

Here is a little tip to display the latest posts of your dotclear blog in your symfony website.

Create a connection

If your blog database is in an other database than symfony's one, you need to create a connection in the /config/databases.yml. Otherwise, skip this step Mine looks like this :

[yml]
dev:
  beta:
    param:
      classname:  DebugPDO

test:
  beta:
    param:
      classname:  DebugPDO

all:
  symfony:
    class:        sfDoctrineDatabase
    param:
      classname:  DoctrinePDO
      dsn:        mysql:dbname=mysymfonydbanem;host=localhost
      username:   mysymfonyusername
      password:   mysymfonypassword
      encoding:   utf8
      persistent: true
      pooling:    true
  blog:
    class:        sfDoctrineDatabase
    param:
      classname:  DoctrinePDO
      dsn:        mysql:dbname=myblogdbname;host=localhost
      username:   myblogusername
      password:   myblogpassword
      encoding:   utf8
      persistent: true
      pooling:    true

Read more about connections.

Create Dotclear models from the database

You must create models from the database to be able to retrieve the posts

$ ./symfony doctrine:build-schema --env=blog
$ ./symfony doctrine:build-model

As you can see, some Dc*.class.php are now created in the /lib/model/doctrine/ folder.

Fetching latests posts

In your controller (actions.class.php), use and customize this request :

public function executeIndex(sfWebRequest $request)
  {
    $this->posts = Doctrine_Query::create()
        ->from('DcPost p')
        ->where('p.post_status = 1')
        ->orderBy('p.post_creadt DESC')
        ->limit(5)
        ->execute();
  }

Displaying posts

<?php foreach($posts as $post) : ?>
  <div class="blog_box">
    <h5><a href="#"><?php echo $post->getPostTitle() ?></a></h5>
    <?php echo __('Posted by') ?> <a href="http://url.to.your.blog/path/index.php?post/<?php echo $post->getPostUrl() ?>"><span><?php echo $post->getDcUser()->getUserDisplayname () ?></span></a> <?php echo __('in') ?> <a href="#"><span><?php echo $post->getDcCategory()->getCatTitle() ?></span></a><br />
    <?php echo substr(strip_tags($post->getRawValue()->getPostContentXhtml()), 0, 40).'...' ?><br />
    <div class="datetime"><?php echo $post->getPostCreadt() ?></div>
  </div>
<?php endforeach; ?>

and that's it, you made it ! :)

PS: This howto is maybe somehow wrong, I made those steps few times before making this working. Feel free to correct.

Update 1 : Added a where clause to select only posts where post_status = 1. Added the link to the post

Protect your .svn files in your website

If you are using like me svn to manage your website, you certainly notified that all the .svn files are fully readable by everyone in plain text. So here is a fix to deny the acces to those files :

<Directory ~ "\.svn">
    Order allow,deny
    Deny from all
</Directory>

Solution found at http://alexking.org/blog/2005/04/12/svn-as-a-web-site-maintenence-tool

- page 1 de 13